summaryrefslogtreecommitdiff
path: root/libjava/classpath/java
diff options
context:
space:
mode:
authormark <mark@138bc75d-0d04-0410-961f-82ee72b054a4>2006-01-17 18:09:40 +0000
committermark <mark@138bc75d-0d04-0410-961f-82ee72b054a4>2006-01-17 18:09:40 +0000
commit2d8cf20d0d5ca6b1fbdefc22229d4b7cf1497ede (patch)
treec976ca91e3ef0bda3b34b37c0195145638d8d08e /libjava/classpath/java
parenta3ef37ddfeddcc5b0f1c5068d8fdeb25a302d5cd (diff)
downloadgcc-2d8cf20d0d5ca6b1fbdefc22229d4b7cf1497ede.tar.gz
Imported GNU Classpath 0.20
* Makefile.am (AM_CPPFLAGS): Add classpath/include. * java/nio/charset/spi/CharsetProvider.java: New override file. * java/security/Security.java: Likewise. * sources.am: Regenerated. * Makefile.in: Likewise. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@109831 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/java')
-rw-r--r--libjava/classpath/java/awt/BorderLayout.java2
-rw-r--r--libjava/classpath/java/awt/Component.java42
-rw-r--r--libjava/classpath/java/awt/Container.java59
-rw-r--r--libjava/classpath/java/awt/GridBagLayout.java24
-rw-r--r--libjava/classpath/java/awt/datatransfer/DataFlavor.java1702
-rw-r--r--libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java292
-rw-r--r--libjava/classpath/java/beans/DefaultPersistenceDelegate.java194
-rw-r--r--libjava/classpath/java/beans/Encoder.java463
-rw-r--r--libjava/classpath/java/beans/EventSetDescriptor.java1173
-rw-r--r--libjava/classpath/java/beans/Expression.java81
-rw-r--r--libjava/classpath/java/beans/IndexedPropertyChangeEvent.java81
-rw-r--r--libjava/classpath/java/beans/Introspector.java204
-rw-r--r--libjava/classpath/java/beans/PersistenceDelegate.java91
-rw-r--r--libjava/classpath/java/beans/PropertyChangeSupport.java52
-rw-r--r--libjava/classpath/java/beans/Statement.java165
-rw-r--r--libjava/classpath/java/beans/XMLEncoder.java265
-rw-r--r--libjava/classpath/java/io/File.java9
-rw-r--r--libjava/classpath/java/io/InputStreamReader.java4
-rw-r--r--libjava/classpath/java/io/ObjectInputStream.java1
-rw-r--r--libjava/classpath/java/io/ObjectOutputStream.java5
-rw-r--r--libjava/classpath/java/io/ObjectStreamClass.java31
-rw-r--r--libjava/classpath/java/io/OutputStreamWriter.java4
-rw-r--r--libjava/classpath/java/io/PrintStream.java9
-rw-r--r--libjava/classpath/java/io/RandomAccessFile.java47
-rw-r--r--libjava/classpath/java/io/StreamTokenizer.java6
-rw-r--r--libjava/classpath/java/lang/Character.java11
-rw-r--r--libjava/classpath/java/lang/Class.java8
-rw-r--r--libjava/classpath/java/lang/Double.java75
-rw-r--r--libjava/classpath/java/lang/Float.java77
-rw-r--r--libjava/classpath/java/lang/InheritableThreadLocal.java13
-rw-r--r--libjava/classpath/java/lang/SecurityManager.java15
-rw-r--r--libjava/classpath/java/lang/StackTraceElement.java22
-rw-r--r--libjava/classpath/java/lang/String.java16
-rw-r--r--libjava/classpath/java/lang/Thread.java6
-rw-r--r--libjava/classpath/java/lang/ThreadLocal.java23
-rw-r--r--libjava/classpath/java/net/DatagramSocket.java7
-rw-r--r--libjava/classpath/java/net/InetAddress.java139
-rw-r--r--libjava/classpath/java/net/URL.java9
-rw-r--r--libjava/classpath/java/nio/charset/Charset.java2
-rw-r--r--libjava/classpath/java/nio/charset/spi/CharsetProvider.java8
-rw-r--r--libjava/classpath/java/security/MessageDigest.java5
-rw-r--r--libjava/classpath/java/security/Security.java26
-rw-r--r--libjava/classpath/java/text/Bidi.java78
-rw-r--r--libjava/classpath/java/text/DecimalFormat.java4
-rw-r--r--libjava/classpath/java/util/AbstractMap.java4
-rw-r--r--libjava/classpath/java/util/ArrayList.java2
-rw-r--r--libjava/classpath/java/util/Collections.java8
-rw-r--r--libjava/classpath/java/util/Hashtable.java232
-rw-r--r--libjava/classpath/java/util/Properties.java226
-rw-r--r--libjava/classpath/java/util/StringTokenizer.java3
-rw-r--r--libjava/classpath/java/util/WeakHashMap.java6
-rw-r--r--libjava/classpath/java/util/logging/XMLFormatter.java2
-rw-r--r--libjava/classpath/java/util/regex/Pattern.java5
53 files changed, 4029 insertions, 2009 deletions
diff --git a/libjava/classpath/java/awt/BorderLayout.java b/libjava/classpath/java/awt/BorderLayout.java
index 1b67c01cfcb..7c8c582a96b 100644
--- a/libjava/classpath/java/awt/BorderLayout.java
+++ b/libjava/classpath/java/awt/BorderLayout.java
@@ -415,7 +415,7 @@ public class BorderLayout implements LayoutManager2, java.io.Serializable
*/
public Dimension maximumLayoutSize(Container target)
{
- return calcSize(target, MAX);
+ return new Dimension (Integer.MAX_VALUE, Integer.MAX_VALUE);
}
/**
diff --git a/libjava/classpath/java/awt/Component.java b/libjava/classpath/java/awt/Component.java
index ec03d631dcf..bd22ea3c984 100644
--- a/libjava/classpath/java/awt/Component.java
+++ b/libjava/classpath/java/awt/Component.java
@@ -1038,14 +1038,10 @@ public abstract class Component
if ((c != null) && c.equals(background))
return;
- // If c is null, inherit from closest ancestor whose bg is set.
- if (c == null && parent != null)
- c = parent.getBackground();
- if (peer != null && c != null)
- peer.setBackground(c);
-
Color previous = background;
background = c;
+ if (peer != null && c != null)
+ peer.setBackground(c);
firePropertyChange("background", previous, c);
}
@@ -2642,7 +2638,7 @@ public abstract class Component
{
mouseMotionListener = AWTEventMulticaster.add(mouseMotionListener, listener);
if (mouseMotionListener != null)
- enableEvents(AWTEvent.MOUSE_EVENT_MASK);
+ enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
/**
@@ -2775,10 +2771,19 @@ public abstract class Component
}
/**
- * Returns all registered EventListers of the given listenerType.
+ * Returns all registered {@link EventListener}s of the given
+ * <code>listenerType</code>.
*
- * @param listenerType the class of listeners to filter
- * @return an array of registered listeners
+ * @param listenerType the class of listeners to filter (<code>null</code>
+ * not permitted).
+ *
+ * @return An array of registered listeners.
+ *
+ * @throws ClassCastException if <code>listenerType</code> does not implement
+ * the {@link EventListener} interface.
+ * @throws NullPointerException if <code>listenerType</code> is
+ * <code>null</code>.
+ *
* @see #getComponentListeners()
* @see #getFocusListeners()
* @see #getHierarchyListeners()
@@ -4786,7 +4791,12 @@ p * <li>the set of backward traversal keys
void dispatchEventImpl(AWTEvent e)
{
Event oldEvent = translateEvent (e);
-
+ // This boolean tells us not to process focus events when the focus
+ // opposite component is the same as the focus component.
+ boolean ignoreFocus =
+ (e instanceof FocusEvent &&
+ ((FocusEvent)e).getComponent() == ((FocusEvent)e).getOppositeComponent());
+
if (oldEvent != null)
postEvent (oldEvent);
@@ -4817,7 +4827,8 @@ p * <li>the set of backward traversal keys
break;
}
}
- if (e.id != PaintEvent.PAINT && e.id != PaintEvent.UPDATE)
+ if (e.id != PaintEvent.PAINT && e.id != PaintEvent.UPDATE
+ && !ignoreFocus)
processEvent(e);
}
@@ -4853,11 +4864,12 @@ p * <li>the set of backward traversal keys
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 MouseEvent.MOUSE_MOVED:
+ case MouseEvent.MOUSE_DRAGGED:
+ return (mouseMotionListener != null
+ || (eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0);
case FocusEvent.FOCUS_GAINED:
case FocusEvent.FOCUS_LOST:
diff --git a/libjava/classpath/java/awt/Container.java b/libjava/classpath/java/awt/Container.java
index ed791dc8b88..67f0ed184d4 100644
--- a/libjava/classpath/java/awt/Container.java
+++ b/libjava/classpath/java/awt/Container.java
@@ -449,9 +449,6 @@ public class Container extends Component
ContainerEvent.COMPONENT_REMOVED,
r);
getToolkit().getSystemEventQueue().postEvent(ce);
-
- // Repaint this container.
- repaint();
}
}
}
@@ -896,13 +893,21 @@ public class Container extends Component
}
/**
- * 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.
+ * Returns all registered {@link EventListener}s of the given
+ * <code>listenerType</code>.
*
+ * @param listenerType the class of listeners to filter (<code>null</code>
+ * not permitted).
+ *
+ * @return An array of registered listeners.
+ *
+ * @throws ClassCastException if <code>listenerType</code> does not implement
+ * the {@link EventListener} interface.
+ * @throws NullPointerException if <code>listenerType</code> is
+ * <code>null</code>.
+ *
+ * @see #getContainerListeners()
+ *
* @since 1.3
*/
public EventListener[] getListeners(Class listenerType)
@@ -1094,7 +1099,7 @@ public class Container extends Component
{
if (!contains(x, y))
return null;
-
+
for (int i = 0; i < ncomponents; ++i)
{
// Ignore invisible children...
@@ -1117,7 +1122,8 @@ public class Container extends Component
}
//don't return transparent components with no MouseListeners
- if (this.getMouseListeners().length == 0)
+ if (getMouseListeners().length == 0
+ && getMouseMotionListeners().length == 0)
return null;
return this;
}
@@ -1625,30 +1631,19 @@ public class Container extends Component
Component comp)
{
Rectangle bounds = comp.getBounds();
- Rectangle oldClip = gfx.getClipBounds();
- if (oldClip == null)
- oldClip = bounds;
-
- Rectangle clip = oldClip.intersection(bounds);
- if (clip.isEmpty()) return;
+ if(!gfx.hitClip(bounds.x,bounds.y, bounds.width, bounds.height))
+ return;
- boolean clipped = false;
- boolean translated = false;
+ Graphics g2 = gfx.create(bounds.x, bounds.y, bounds.width,
+ bounds.height);
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);
+ visitor.visit(comp, g2);
}
finally
{
- if (translated)
- gfx.translate (-bounds.x, -bounds.y);
- if (clipped)
- gfx.setClip (oldClip.x, oldClip.y, oldClip.width, oldClip.height);
+ g2.dispose();
}
}
@@ -2148,12 +2143,18 @@ class LightweightDispatcher implements Serializable
break;
}
- if (me.getID() == MouseEvent.MOUSE_PRESSED && modifiers > 0
+ if (me.getID() == MouseEvent.MOUSE_RELEASED
+ || me.getID() == MouseEvent.MOUSE_PRESSED && modifiers > 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: This is important for correct dragging
+ // behaviour, otherwise the release goes to an arbitrary component
+ // outside of the dragged component. OTOH, if there is no mouse
+ // drag while the mouse is pressed, the component under the mouse
+ // is the same as the previously pressed component anyway.
// - MOUSE_PRESSED: another button pressed while the first is held
// down
// - MOUSE_DRAGGED
diff --git a/libjava/classpath/java/awt/GridBagLayout.java b/libjava/classpath/java/awt/GridBagLayout.java
index 083c0b7a7a3..714e080d7b2 100644
--- a/libjava/classpath/java/awt/GridBagLayout.java
+++ b/libjava/classpath/java/awt/GridBagLayout.java
@@ -341,11 +341,14 @@ public class GridBagLayout
GridBagLayoutInfo info = getLayoutInfo (parent, PREFERREDSIZE);
if (info.cols == 0 && info.rows == 0)
return;
- layoutInfo = info;
// DEBUG
- //dumpLayoutInfo (layoutInfo);
-
+ //dumpLayoutInfo (info);
+
+ // Calling setBounds on these components causes this layout to
+ // be invalidated, clearing the layout information cache,
+ // layoutInfo. So we wait until after this for loop to set
+ // layoutInfo.
for(int i = 0; i < components.length; i++)
{
Component component = components [i];
@@ -357,11 +360,11 @@ public class GridBagLayout
GridBagConstraints constraints =
lookupInternalConstraints(component);
- int cellx = sumIntArray(layoutInfo.colWidths, constraints.gridx);
- int celly = sumIntArray(layoutInfo.rowHeights, constraints.gridy);
- int cellw = sumIntArray(layoutInfo.colWidths,
+ int cellx = sumIntArray(info.colWidths, constraints.gridx);
+ int celly = sumIntArray(info.rowHeights, constraints.gridy);
+ int cellw = sumIntArray(info.colWidths,
constraints.gridx + constraints.gridwidth) - cellx;
- int cellh = sumIntArray(layoutInfo.rowHeights,
+ int cellh = sumIntArray(info.rowHeights,
constraints.gridy + constraints.gridheight) - celly;
Insets insets = constraints.insets;
@@ -438,11 +441,14 @@ public class GridBagLayout
break;
}
- component.setBounds(layoutInfo.pos_x + x, layoutInfo.pos_y + y, dim.width, dim.height);
+ component.setBounds(info.pos_x + x, info.pos_y + y, dim.width, dim.height);
}
// DEBUG
- //dumpLayoutInfo (layoutInfo);
+ //dumpLayoutInfo (info);
+
+ // Cache layout information.
+ layoutInfo = getLayoutInfo (parent, PREFERREDSIZE);
}
/**
diff --git a/libjava/classpath/java/awt/datatransfer/DataFlavor.java b/libjava/classpath/java/awt/datatransfer/DataFlavor.java
index 38f415b7526..32bf4d6cf37 100644
--- a/libjava/classpath/java/awt/datatransfer/DataFlavor.java
+++ b/libjava/classpath/java/awt/datatransfer/DataFlavor.java
@@ -1,5 +1,5 @@
/* DataFlavor.java -- A type of data to transfer via the clipboard.
- Copyright (C) 1999, 2001, 2004 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2001, 2004, 2005 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -49,6 +49,7 @@ import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import java.nio.charset.Charset;
import java.rmi.Remote;
/**
@@ -63,946 +64,818 @@ public class DataFlavor implements java.io.Externalizable, Cloneable
// 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,
- "application/x-java-file-list; class=java.util.List",
- "Java File List");
-
- imageFlavor
- = new DataFlavor(java.awt.Image.class,
- "Java Image");
-}
-
-/*************************************************************************/
+ /**
+ * 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 =
+ new DataFlavor(java.io.InputStream.class,
+ "text/plain; charset=unicode",
+ "plain unicode text");
-/*
- * Instance Variables
- */
+ /**
+ * 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 =
+ new DataFlavor(java.lang.String.class, "Java Unicode String");
-// The MIME type for this flavor
-private final String mimeType;
+ /**
+ * 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 =
+ new DataFlavor(java.util.List.class,
+ "application/x-java-file-list; class=java.util.List",
+ "Java File List");
-// The representation class for this flavor
-private final Class representationClass;
+ /**
+ * This is an image flavor used for transferring images. The
+ * representation type is a <code>java.awt.Image</code>.
+ */
+ public static final DataFlavor imageFlavor =
+ new DataFlavor(java.awt.Image.class, "Java Image");
-// The human readable name of this flavor
-private String humanPresentableName;
+ /**
+ * 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";
-/*
- * Static Methods
- */
+ /**
+ * 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";
-/**
- * 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) { ; }
- */
+ * 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;
- // FIXME: What is the context class loader?
/*
- try
+ * 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);
+ }
+
+ 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;
}
- catch(Exception e) { ; }
- */
-
- if (classLoader != null)
- return(classLoader.loadClass(className));
- else
- throw new ClassNotFoundException(className);
-}
-
-/*************************************************************************/
+
+ /**
+ * 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));
+ }
+
+ /**
+ * XXX - Currently returns <code>plainTextFlavor</code>.
+ */
+ public static final DataFlavor getTextPlainUnicodeFlavor()
+ {
+ return plainTextFlavor;
+ }
+
+ /**
+ * 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;
+ }
-/*
- * Constructors
- */
-/**
- * Empty public constructor needed for externalization.
- * Should not be used for normal instantiation.
- */
-public
-DataFlavor()
-{
+ /*
+ * 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)
-{
+ /**
+ * Private constructor.
+ */
+ private DataFlavor(Class representationClass,
+ String mimeType,
+ String humanPresentableName)
+ {
this.representationClass = representationClass;
this.mimeType = mimeType;
if (humanPresentableName != null)
- this.humanPresentableName = humanPresentableName;
+ this.humanPresentableName = humanPresentableName;
else
- this.humanPresentableName = mimeType;
-}
-
-/*************************************************************************/
+ 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)
-{
+ /**
+ * 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=&lt;rep class&gt;" 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=&lt;rep class&gt;" 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),
- mimeType, 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);
-}
+ "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=&lt;rep class&gt;" 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);
+ }
-/**
- * Returns the human presentable name for this flavor.
- *
- * @return The human presentable name for this flavor.
- */
-public String
-getHumanPresentableName()
-{
- return(humanPresentableName);
-}
+ /**
+ * Initializes a new instance of <code>DataFlavor</code> with the
+ * specified MIME type and description. If the MIME type has a
+ * "class=&lt;rep class&gt;" 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),
+ mimeType, 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 primary MIME type for this flavor.
- *
- * @return The primary MIME type for this flavor.
- */
-public String
-getPrimaryType()
-{
- int idx = mimeType.indexOf("/");
- if (idx == -1)
+ /**
+ * Returns the MIME type of this flavor.
+ *
+ * @return The MIME type for this flavor.
+ */
+ public String getMimeType()
+ {
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 start = mimeType.indexOf("/");
- if (start == -1)
- return "";
-
- int end = mimeType.indexOf(";", start + 1);
- if (end == -1)
- return mimeType.substring(start + 1);
- else
- return mimeType.substring(start + 1, end);
-}
-
-/*************************************************************************/
-
-/**
- * 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)
-{
- if ("humanPresentableName".equals(paramName))
- return getHumanPresentableName();
-
- 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. Ignores parameters.
- *
- * @param mimeType The MIME type to test against.
- *
- * @return <code>true</code> if the MIME type is equal to this object's
- * MIME type (ignoring parameters), <code>false</code> otherwise.
- *
- * @exception NullPointerException If mimeType is null.
- */
-public boolean
-isMimeTypeEqual(String mimeType)
-{
- String mime = getMimeType();
- int i = mime.indexOf(";");
- if (i != -1)
- mime = mime.substring(0, i);
-
- i = mimeType.indexOf(";");
- if (i != -1)
- mimeType = mimeType.substring(0, i);
-
- return mime.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()
-{
- return Remote.class.isAssignableFrom (representationClass);
-}
-
-/*************************************************************************/
-
-/**
- * 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);
+ /**
+ * Returns the representation class for this flavor.
+ *
+ * @return The representation class for this flavor.
+ */
+ public Class getRepresentationClass()
+ {
+ return(representationClass);
+ }
- return(true);
-}
+ /**
+ * 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));
+ }
-/**
- * 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);
+ /**
+ * Returns the MIME subtype for this flavor.
+ *
+ * @return The MIME subtype for this flavor.
+ */
+ public String getSubType()
+ {
+ int start = mimeType.indexOf("/");
+ if (start == -1)
+ return "";
+
+ int end = mimeType.indexOf(";", start + 1);
+ if (end == -1)
+ return mimeType.substring(start + 1);
+ else
+ return mimeType.substring(start + 1, end);
+ }
- return(equals((DataFlavor)obj));
-}
+ /**
+ * 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)
+ {
+ if ("humanPresentableName".equals(paramName))
+ return getHumanPresentableName();
+
+ 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 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));
-}
+ /**
+ * Tests the MIME type of this object for equality against the specified
+ * MIME type. Ignores parameters.
+ *
+ * @param mimeType The MIME type to test against.
+ *
+ * @return <code>true</code> if the MIME type is equal to this object's
+ * MIME type (ignoring parameters), <code>false</code> otherwise.
+ *
+ * @exception NullPointerException If mimeType is null.
+ */
+ public boolean isMimeTypeEqual(String mimeType)
+ {
+ String mime = getMimeType();
+ int i = mime.indexOf(";");
+ if (i != -1)
+ mime = mime.substring(0, i);
+
+ i = mimeType.indexOf(";");
+ if (i != -1)
+ mimeType = mimeType.substring(0, i);
+
+ return mime.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());
+ }
-/**
- * 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());
-}
+ /**
+ * 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");
+ }
-/**
- * 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));
-}
+ /**
+ * 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()
+ {
+ return Remote.class.isAssignableFrom (representationClass);
+ }
-/**
- * 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);
-}
+ /**
+ * 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));
+ }
-/**
- * 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);
-}
+ /**
+ * 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 (mimeType.equals(javaFileListFlavor.mimeType)
+ && 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
+ {
+ // FIXME - This cannot be right.
+ try
+ {
+ return super.clone();
+ }
+ catch(Exception e)
+ {
+ return null;
+ }
+ }
-/**
- * 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
-}
+ /**
+ * 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);
+ }
-/**
- * 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
-}
+ /**
+ * 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 a string representation of this DataFlavor. Including the
- * representation class name, MIME type and human presentable name.
- */
-public String
-toString()
-{
- return(getClass().getName()
- + "[representationClass=" + getRepresentationClass().getName()
- + ",mimeType=" + getMimeType()
- + ",humanPresentableName=" + getHumanPresentableName()
- + "]");
-}
+ /**
+ * 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;
+ }
-/**
- * XXX - Currently returns <code>plainTextFlavor</code>.
- */
-public static final DataFlavor
-getTextPlainUnicodeFlavor()
-{
- return(plainTextFlavor);
-}
+ /**
+ * 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
+ }
-/**
- * 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());
-}
+ /**
+ * 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 (getClass().getName()
+ + "[representationClass=" + getRepresentationClass().getName()
+ + ",mimeType=" + getMimeType()
+ + ",humanPresentableName=" + getHumanPresentableName()
+ + "]");
+ }
-/**
- * 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;
+ /**
+ * XXX - Currently returns <code>java.io.InputStream</code>.
+ *
+ * @since 1.3
+ */
+ public final Class getDefaultRepresentationClass()
+ {
+ return java.io.InputStream.class;
+ }
- // A Reader or String is good.
- if ((Reader.class.isAssignableFrom(c))
- || (String.class.isAssignableFrom(c)))
- {
- return df;
- }
+ /**
+ * XXX - Currently returns <code>java.io.InputStream</code>.
+ */
+ public final String getDefaultRepresentationClassAsString()
+ {
+ return getDefaultRepresentationClass().getName();
+ }
- // A InputStream is good if the mime primary type is "text"
- if ((InputStream.class.isAssignableFrom(c))
- && ("text".equals(df.getPrimaryType())))
+ /**
+ * 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()))
{
- String encoding = availableFlavors[i].getParameter("charset");
+ InputStream in = (InputStream)transferable.getTransferData(this);
+ String encoding = 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;
+ encoding = "us-ascii";
+ return new InputStreamReader(in, encoding);
}
- }
-
- // 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);
-}
+
+ throw new UnsupportedFlavorException(this);
+ }
/**
* Returns whether the representation class for this DataFlavor is
@@ -1010,9 +883,9 @@ public Reader getReaderForText(Transferable transferable)
*
* @since 1.4
*/
- public boolean isRepresentationClassByteBuffer ()
+ public boolean isRepresentationClassByteBuffer()
{
- return ByteBuffer.class.isAssignableFrom (representationClass);
+ return ByteBuffer.class.isAssignableFrom(representationClass);
}
/**
@@ -1021,9 +894,9 @@ public Reader getReaderForText(Transferable transferable)
*
* @since 1.4
*/
- public boolean isRepresentationClassCharBuffer ()
+ public boolean isRepresentationClassCharBuffer()
{
- return CharBuffer.class.isAssignableFrom (representationClass);
+ return CharBuffer.class.isAssignableFrom(representationClass);
}
/**
@@ -1032,10 +905,67 @@ public Reader getReaderForText(Transferable transferable)
*
* @since 1.4
*/
- public boolean isRepresentationClassReader ()
+ public boolean isRepresentationClassReader()
{
- return Reader.class.isAssignableFrom (representationClass);
+ return Reader.class.isAssignableFrom(representationClass);
+ }
+
+ /**
+ * Returns whether this <code>DataFlavor</code> is a valid text flavor for
+ * this implementation of the Java platform. Only flavors equivalent to
+ * <code>DataFlavor.stringFlavor</code> and <code>DataFlavor</code>s with
+ * a primary MIME type of "text" can be valid text flavors.
+ * <p>
+ * If this flavor supports the charset parameter, it must be equivalent to
+ * <code>DataFlavor.stringFlavor</code>, or its representation must be
+ * <code>java.io.Reader</code>, <code>java.lang.String</code>,
+ * <code>java.nio.CharBuffer</code>, <code>java.io.InputStream</code> or
+ * <code>java.nio.ByteBuffer</code>,
+ * If the representation is <code>java.io.InputStream</code> or
+ * <code>java.nio.ByteBuffer</code>, then this flavor's <code>charset</code>
+ * parameter must be supported by this implementation of the Java platform.
+ * If a charset is not specified, then the platform default charset, which
+ * is always supported, is assumed.
+ * <p>
+ * If this flavor does not support the charset parameter, its
+ * representation must be <code>java.io.InputStream</code>,
+ * <code>java.nio.ByteBuffer</code>.
+ * <p>
+ * See <code>selectBestTextFlavor</code> for a list of text flavors which
+ * support the charset parameter.
+ *
+ * @return <code>true</code> if this <code>DataFlavor</code> is a valid
+ * text flavor as described above; <code>false</code> otherwise
+ * @see #selectBestTextFlavor
+ * @since 1.4
+ */
+ public boolean isFlavorTextType() {
+ // FIXME: I'm not 100% sure if this implementation does the same like sun's does
+ if(equals(DataFlavor.stringFlavor) || getPrimaryType().equals("text"))
+ {
+ String charset = getParameter("charset");
+ Class c = getRepresentationClass();
+ if(charset != null)
+ {
+ if(Reader.class.isAssignableFrom(c)
+ || CharBuffer.class.isAssignableFrom(c)
+ || String.class.isAssignableFrom(c))
+ {
+ return true;
+ }
+ else if(InputStream.class.isAssignableFrom(c)
+ || ByteBuffer.class.isAssignableFrom(c))
+ {
+ return Charset.isSupported(charset);
+ }
+ }
+ else if(InputStream.class.isAssignableFrom(c)
+ || ByteBuffer.class.isAssignableFrom(c))
+ {
+ return true;
+ }
+ }
+ return false;
}
-
} // class DataFlavor
diff --git a/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java b/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java
index f6530f5117c..7b4d2fbd38f 100644
--- a/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java
+++ b/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java
@@ -38,9 +38,11 @@ exception statement from your version. */
package java.awt.datatransfer;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.WeakHashMap;
/**
* This class maps between native platform type names and DataFlavors.
@@ -54,10 +56,28 @@ import java.util.Map;
public final class SystemFlavorMap implements FlavorMap, FlavorTable
{
/**
- * The default (instance) flavor map.
+ * The map which maps the thread's <code>ClassLoaders</code> to
+ * <code>SystemFlavorMaps</code>.
*/
- private static FlavorMap defaultFlavorMap;
-
+ private static final Map systemFlavorMaps = new WeakHashMap();
+
+ /**
+ * Constant which is used to prefix encode Java MIME types.
+ */
+ private static final String GNU_JAVA_MIME_PREFIX = "gnu.java:";
+
+ /**
+ * This map maps native <code>String</code>s to lists of
+ * <code>DataFlavor</code>s
+ */
+ private HashMap nativeToFlavorMap = new HashMap();
+
+ /**
+ * This map maps <code>DataFlavor</code>s to lists of native
+ * <code>String</code>s
+ */
+ private HashMap flavorToNativeMap = new HashMap();
+
/**
* Private constructor.
*/
@@ -98,47 +118,118 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable
}
/**
- * Returns the default (instance) (System)FlavorMap.
+ * Returns the (System)FlavorMap for the current thread's
+ * ClassLoader.
*/
public static FlavorMap getDefaultFlavorMap ()
{
- if (defaultFlavorMap == null)
- defaultFlavorMap = new SystemFlavorMap ();
-
- return defaultFlavorMap;
+ ClassLoader classLoader = Thread.currentThread()
+ .getContextClassLoader();
+
+ //if ContextClassLoader not set, use system default
+ if (classLoader == null)
+ {
+ classLoader = ClassLoader.getSystemClassLoader();
+ }
+
+ synchronized(systemFlavorMaps)
+ {
+ FlavorMap map = (FlavorMap)
+ systemFlavorMaps.get(classLoader);
+ if (map == null)
+ {
+ map = new SystemFlavorMap();
+ systemFlavorMaps.put(classLoader, map);
+ }
+ return map;
+ }
}
/**
- * Returns the native type name for the given java mime type.
+ * Encodes a MIME type for use as a <code>String</code> native. The format
+ * of an encoded representation of a MIME type is implementation-dependent.
+ * The only restrictions are:
+ * <ul>
+ * <li>The encoded representation is <code>null</code> if and only if the
+ * MIME type <code>String</code> is <code>null</code>.</li>
+ * <li>The encoded representations for two non-<code>null</code> MIME type
+ * <code>String</code>s are equal if and only if these <code>String</code>s
+ * are equal according to <code>String.equals(Object)</code>.</li>
+ * </ul>
+ * <p>
+ * The present implementation of this method returns the specified MIME
+ * type <code>String</code> prefixed with <code>gnu.java:</code>.
+ *
+ * @param mime the MIME type to encode
+ * @return the encoded <code>String</code>, or <code>null</code> if
+ * mimeType is <code>null</code>
*/
public static String encodeJavaMIMEType (String mime)
{
- return null;
+ if (mime != null)
+ return GNU_JAVA_MIME_PREFIX + mime;
+ else
+ return null;
}
/**
- * Returns the native type name for the given data flavor.
+ * Encodes a <code>DataFlavor</code> for use as a <code>String</code>
+ * native. The format of an encoded <code>DataFlavor</code> is
+ * implementation-dependent. The only restrictions are:
+ * <ul>
+ * <li>The encoded representation is <code>null</code> if and only if the
+ * specified <code>DataFlavor</code> is <code>null</code> or its MIME type
+ * <code>String</code> is <code>null</code>.</li>
+ * <li>The encoded representations for two non-<code>null</code>
+ * <code>DataFlavor</code>s with non-<code>null</code> MIME type
+ * <code>String</code>s are equal if and only if the MIME type
+ * <code>String</code>s of these <code>DataFlavor</code>s are equal
+ * according to <code>String.equals(Object)</code>.</li>
+ * </ul>
+ * <p>
+ * The present implementation of this method returns the MIME type
+ * <code>String</code> of the specified <code>DataFlavor</code> prefixed
+ * with <code>gnu.java:</code>.
+ *
+ * @param df the <code>DataFlavor</code> to encode
+ * @return the encoded <code>String</code>, or <code>null</code> if
+ * flav is <code>null</code> or has a <code>null</code> MIME type
*/
public static String encodeDataFlavor (DataFlavor df)
{
- return null;
+ if (df != null)
+ {
+ return encodeJavaMIMEType(df.getMimeType());
+ }
+ else
+ return null;
}
/**
* Returns true if the native type name can be represented as
- * a java mime type.
+ * a java mime type. Returns <code>false</code> if parameter is
+ * <code>null</code>.
*/
public static boolean isJavaMIMEType (String name)
{
- return false;
+ return (name != null && name.startsWith(GNU_JAVA_MIME_PREFIX));
}
/**
- * Returns the java mime type for the given the native type name.
+ * Decodes a <code>String</code> native for use as a Java MIME type.
+ *
+ * @param name the <code>String</code> to decode
+ * @return the decoded Java MIME type, or <code>null</code> if nat
+ * is not an encoded <code>String</code> native
*/
public static String decodeJavaMIMEType (String name)
{
- return null;
+ if (isJavaMIMEType(name))
+ {
+ return name.substring(GNU_JAVA_MIME_PREFIX.length());
+ }
+ else
+ return null;
}
/**
@@ -156,6 +247,20 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable
return null;
}
+ /**
+ * Returns a List of <code>DataFlavors</code> to which the specified
+ * <code>String</code> native can be translated by the data transfer
+ * subsystem. The <code>List</code> will be sorted from best
+ * <code>DataFlavor</code> to worst. That is, the first <code>DataFlavor
+ * </code> will best reflect data in the specified native to a Java
+ * application.
+ * <p>
+ * If the specified native is previously unknown to the data transfer
+ * subsystem, and that native has been properly encoded, then invoking
+ * this method will establish a mapping in both directions between the
+ * specified native and a DataFlavor whose MIME type is a decoded
+ * version of the native.
+ */
public List getFlavorsForNative (String nat)
{
throw new Error ("Not implemented");
@@ -165,5 +270,160 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable
{
throw new Error ("Not implemented");
}
+
+ /**
+ * Adds a mapping from a single <code>String</code> native to a single
+ * <code>DataFlavor</code>. Unlike <code>getFlavorsForNative</code>, the
+ * mapping will only be established in one direction, and the native will
+ * not be encoded. To establish a two-way mapping, call
+ * <code>addUnencodedNativeForFlavor</code> as well. The new mapping will
+ * be of lower priority than any existing mapping.
+ * This method has no effect if a mapping from the specified
+ * <code>String</code> native to the specified or equal
+ * <code>DataFlavor</code> already exists.
+ *
+ * @param nativeStr the <code>String</code> native key for the mapping
+ * @param flavor the <code>DataFlavor</code> value for the mapping
+ * @throws NullPointerException if nat or flav is <code>null</code>
+ *
+ * @see #addUnencodedNativeForFlavor
+ * @since 1.4
+ */
+ public synchronized void addFlavorForUnencodedNative(String nativeStr,
+ DataFlavor flavor)
+ {
+ if ((nativeStr == null) || (flavor == null))
+ throw new NullPointerException();
+ List flavors = (List) nativeToFlavorMap.get(nativeStr);
+ if (flavors == null)
+ {
+ flavors = new ArrayList();
+ nativeToFlavorMap.put(nativeStr, flavors);
+ }
+ else
+ {
+ if (! flavors.contains(flavor))
+ flavors.add(flavor);
+ }
+ }
+
+ /**
+ * Adds a mapping from the specified <code>DataFlavor</code> (and all
+ * <code>DataFlavor</code>s equal to the specified <code>DataFlavor</code>)
+ * to the specified <code>String</code> native.
+ * Unlike <code>getNativesForFlavor</code>, the mapping will only be
+ * established in one direction, and the native will not be encoded. To
+ * establish a two-way mapping, call
+ * <code>addFlavorForUnencodedNative</code> as well. The new mapping will
+ * be of lower priority than any existing mapping.
+ * This method has no effect if a mapping from the specified or equal
+ * <code>DataFlavor</code> to the specified <code>String</code> native
+ * already exists.
+ *
+ * @param flavor the <code>DataFlavor</code> key for the mapping
+ * @param nativeStr the <code>String</code> native value for the mapping
+ * @throws NullPointerException if flav or nat is <code>null</code>
+ *
+ * @see #addFlavorForUnencodedNative
+ * @since 1.4
+ */
+ public synchronized void addUnencodedNativeForFlavor(DataFlavor flavor,
+ String nativeStr)
+ {
+ if ((nativeStr == null) || (flavor == null))
+ throw new NullPointerException();
+ List natives = (List) flavorToNativeMap.get(flavor);
+ if (natives == null)
+ {
+ natives = new ArrayList();
+ flavorToNativeMap.put(flavor, natives);
+ }
+ else
+ {
+ if (! natives.contains(nativeStr))
+ natives.add(nativeStr);
+ }
+ }
+
+ /**
+ * Discards the current mappings for the specified <code>DataFlavor</code>
+ * and all <code>DataFlavor</code>s equal to the specified
+ * <code>DataFlavor</code>, and creates new mappings to the
+ * specified <code>String</code> natives.
+ * Unlike <code>getNativesForFlavor</code>, the mappings will only be
+ * established in one direction, and the natives will not be encoded. To
+ * establish two-way mappings, call <code>setFlavorsForNative</code>
+ * as well. The first native in the array will represent the highest
+ * priority mapping. Subsequent natives will represent mappings of
+ * decreasing priority.
+ * <p>
+ * If the array contains several elements that reference equal
+ * <code>String</code> natives, this method will establish new mappings
+ * for the first of those elements and ignore the rest of them.
+ * <p>
+ * It is recommended that client code not reset mappings established by the
+ * data transfer subsystem. This method should only be used for
+ * application-level mappings.
+ *
+ * @param flavor the <code>DataFlavor</code> key for the mappings
+ * @param natives the <code>String</code> native values for the mappings
+ * @throws NullPointerException if flav or natives is <code>null</code>
+ * or if natives contains <code>null</code> elements
+ *
+ * @see #setFlavorsForNative
+ * @since 1.4
+ */
+ public synchronized void setNativesForFlavor(DataFlavor flavor,
+ String[] natives)
+ {
+ if ((natives == null) || (flavor == null))
+ throw new NullPointerException();
+
+ flavorToNativeMap.remove(flavor);
+ for (int i = 0; i < natives.length; i++)
+ {
+ addUnencodedNativeForFlavor(flavor, natives[i]);
+ }
+ }
+
+ /**
+ * Discards the current mappings for the specified <code>String</code>
+ * native, and creates new mappings to the specified
+ * <code>DataFlavor</code>s. Unlike <code>getFlavorsForNative</code>, the
+ * mappings will only be established in one direction, and the natives need
+ * not be encoded. To establish two-way mappings, call
+ * <code>setNativesForFlavor</code> as well. The first
+ * <code>DataFlavor</code> in the array will represent the highest priority
+ * mapping. Subsequent <code>DataFlavor</code>s will represent mappings of
+ * decreasing priority.
+ * <p>
+ * If the array contains several elements that reference equal
+ * <code>DataFlavor</code>s, this method will establish new mappings
+ * for the first of those elements and ignore the rest of them.
+ * <p>
+ * It is recommended that client code not reset mappings established by the
+ * data transfer subsystem. This method should only be used for
+ * application-level mappings.
+ *
+ * @param nativeStr the <code>String</code> native key for the mappings
+ * @param flavors the <code>DataFlavor</code> values for the mappings
+ * @throws NullPointerException if nat or flavors is <code>null</code>
+ * or if flavors contains <code>null</code> elements
+ *
+ * @see #setNativesForFlavor
+ * @since 1.4
+ */
+ public synchronized void setFlavorsForNative(String nativeStr,
+ DataFlavor[] flavors)
+ {
+ if ((nativeStr == null) || (flavors == null))
+ throw new NullPointerException();
+
+ nativeToFlavorMap.remove(nativeStr);
+ for (int i = 0; i < flavors.length; i++)
+ {
+ addFlavorForUnencodedNative(nativeStr, flavors[i]);
+ }
+ }
} // class SystemFlavorMap
diff --git a/libjava/classpath/java/beans/DefaultPersistenceDelegate.java b/libjava/classpath/java/beans/DefaultPersistenceDelegate.java
new file mode 100644
index 00000000000..9dd1ae564f7
--- /dev/null
+++ b/libjava/classpath/java/beans/DefaultPersistenceDelegate.java
@@ -0,0 +1,194 @@
+/* DefaultPersistenceDelegate.java
+ 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. */
+
+
+package java.beans;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/** <p><code>DefaultPersistenceDelegate</code> is a {@link PersistenceDelegate}
+ * implementation that can be used to serialize objects which adhere to the
+ * Java Beans naming convention.</p>
+ *
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.4
+ */
+public class DefaultPersistenceDelegate extends PersistenceDelegate
+{
+
+ private String[] constructorPropertyNames;
+
+ /** Using this constructor the object to be serialized will be instantiated
+ * with the default non-argument constructor.
+ */
+ public DefaultPersistenceDelegate()
+ {
+ }
+
+ /** This constructor allows to specify which Bean properties appear
+ * in the constructor.
+ *
+ * <p>The implementation reads the mentioned properties from the Bean
+ * instance and applies it in the given order to a corresponding
+ * constructor.</p>
+ *
+ * @param constructorPropertyNames The properties the Bean's constructor
+ * should be given to.
+ */
+ public DefaultPersistenceDelegate(String[] constructorPropertyNames)
+ {
+ this.constructorPropertyNames = constructorPropertyNames;
+ }
+
+ protected boolean mutatesTo(Object oldInstance, Object newInstance)
+ {
+ try
+ {
+
+ return (constructorPropertyNames != null
+ && constructorPropertyNames.length > 0
+ && oldInstance.getClass()
+ .getDeclaredMethod("equals",
+ new Class[] { Object.class }) != null)
+ ? oldInstance.equals(newInstance)
+ : super.mutatesTo(oldInstance, newInstance);
+ }
+ catch (NoSuchMethodException nsme)
+ {
+ return super.mutatesTo(oldInstance, newInstance);
+ }
+ }
+
+ protected Expression instantiate(Object oldInstance, Encoder out)
+ {
+ Object[] args = null;
+
+ try
+ {
+ // If there are property names in the array, then we create
+ // a corresponding argument array and store every
+ // argument in it. To retrieve an argument object we have
+ // dig up the right property in the bean class' BeanInfo
+ // object.
+ // This is so costly in terms of execution time I better
+ // not think twice about it ...
+ if (constructorPropertyNames != null)
+ {
+ args = new Object[constructorPropertyNames.length];
+
+ // Look up the properties of oldInstance's class to find matches for
+ // the
+ // names given in the constructor.
+ PropertyDescriptor[] propertyDescs = Introspector.getBeanInfo(
+ oldInstance.getClass()).getPropertyDescriptors();
+
+ for (int i = 0; i < constructorPropertyNames.length; i++)
+ {
+ // Scan the property descriptions for a matching name.
+ for (int j = 0; j < propertyDescs.length; j++)
+ {
+ if (propertyDescs[i].getName().equals(
+ constructorPropertyNames[i]))
+ {
+ Method readMethod = propertyDescs[i].getReadMethod();
+
+ args[i] = readMethod.invoke(oldInstance, null);
+ }
+ }
+ }
+ }
+
+ }
+ catch (IllegalAccessException iae)
+ {
+ out.getExceptionListener().exceptionThrown(iae);
+ }
+ catch (IllegalArgumentException iarge)
+ {
+ out.getExceptionListener().exceptionThrown(iarge);
+ }
+ catch (InvocationTargetException ite)
+ {
+ out.getExceptionListener().exceptionThrown(ite);
+ }
+ catch (IntrospectionException ie)
+ {
+ out.getExceptionListener().exceptionThrown(ie);
+ }
+
+ return new Expression(oldInstance, oldInstance.getClass(), "new", args);
+ }
+
+ protected void initialize(Class type, Object oldInstance, Object newInstance,
+ Encoder out)
+ {
+ try
+ {
+ PropertyDescriptor[] propertyDescs = Introspector.getBeanInfo(
+ oldInstance.getClass()).getPropertyDescriptors();
+
+ for (int i = 0; i < propertyDescs.length; i++)
+ {
+ Method readMethod = propertyDescs[i].getReadMethod();
+ Method writeMethod = propertyDescs[i].getWriteMethod();
+
+ if (readMethod != null && writeMethod != null)
+ {
+ Object oldValue = readMethod.invoke(oldInstance, null);
+
+ if (oldValue != null)
+ out.writeStatement(new Statement(oldInstance,
+ writeMethod.getName(),
+ new Object[] { oldValue }));
+ }
+ }
+ }
+ catch (IntrospectionException ie)
+ {
+ out.getExceptionListener().exceptionThrown(ie);
+ }
+ catch (IllegalAccessException iae)
+ {
+ out.getExceptionListener().exceptionThrown(iae);
+ }
+ catch (InvocationTargetException ite)
+ {
+ out.getExceptionListener().exceptionThrown(ite);
+ }
+ }
+}
diff --git a/libjava/classpath/java/beans/Encoder.java b/libjava/classpath/java/beans/Encoder.java
new file mode 100644
index 00000000000..9b96aaa8ea6
--- /dev/null
+++ b/libjava/classpath/java/beans/Encoder.java
@@ -0,0 +1,463 @@
+/* Encoder.java
+ 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. */
+
+
+package java.beans;
+
+import gnu.java.beans.encoder.ArrayPersistenceDelegate;
+import gnu.java.beans.encoder.ClassPersistenceDelegate;
+import gnu.java.beans.encoder.CollectionPersistenceDelegate;
+import gnu.java.beans.encoder.MapPersistenceDelegate;
+import gnu.java.beans.encoder.PrimitivePersistenceDelegate;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+
+/**
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.4
+ */
+public class Encoder
+{
+
+ /**
+ * An internal DefaultPersistenceDelegate instance that is used for every
+ * class that does not a have a special special PersistenceDelegate.
+ */
+ private static PersistenceDelegate defaultPersistenceDelegate;
+
+ private static PersistenceDelegate fakePersistenceDelegate;
+
+ /**
+ * Stores the relation Class->PersistenceDelegate.
+ */
+ private static HashMap delegates = new HashMap();
+
+ /**
+ * Stores the relation oldInstance->newInstance
+ */
+ private IdentityHashMap candidates = new IdentityHashMap();
+
+ private ExceptionListener exceptionListener;
+
+ /**
+ * A simple number that is used to restrict the access to writeExpression and
+ * writeStatement. The rule is that both methods should only be used when an
+ * object is written to the stream (= writeObject). Therefore accessCounter is
+ * incremented just before the call to writeObject and decremented afterwards.
+ * Then writeStatement and writeExpression allow execution only if
+ * accessCounter is bigger than zero.
+ */
+ private int accessCounter = 0;
+
+ public Encoder()
+ {
+ setupDefaultPersistenceDelegates();
+
+ setExceptionListener(null);
+ }
+
+ /**
+ * Sets up a bunch of {@link PersistenceDelegate} instances which are needed
+ * for the basic working of a {@link Encoder}s.
+ */
+ private static void setupDefaultPersistenceDelegates()
+ {
+ synchronized (delegates)
+ {
+ if (defaultPersistenceDelegate != null)
+ return;
+
+ delegates.put(Class.class, new ClassPersistenceDelegate());
+
+ PersistenceDelegate pd = new PrimitivePersistenceDelegate();
+ delegates.put(Boolean.class, pd);
+ delegates.put(Byte.class, pd);
+ delegates.put(Short.class, pd);
+ delegates.put(Integer.class, pd);
+ delegates.put(Long.class, pd);
+ delegates.put(Float.class, pd);
+ delegates.put(Double.class, pd);
+
+ delegates.put(Object[].class, new ArrayPersistenceDelegate());
+
+ pd = new CollectionPersistenceDelegate();
+ delegates.put(ArrayList.class, pd);
+ delegates.put(LinkedList.class, pd);
+ delegates.put(Vector.class, pd);
+ delegates.put(HashSet.class, pd);
+ delegates.put(LinkedHashSet.class, pd);
+ delegates.put(TreeSet.class, pd);
+
+ pd = new MapPersistenceDelegate();
+ delegates.put(HashMap.class, pd);
+ delegates.put(TreeMap.class, pd);
+ delegates.put(java.util.Hashtable.class, pd);
+ delegates.put(java.util.IdentityHashMap.class, pd);
+
+ delegates.put(java.util.LinkedHashMap.class, pd);
+ delegates.put(java.util.Properties.class, pd);
+
+ delegates.put(java.awt.RenderingHints.class, pd);
+ delegates.put(java.util.WeakHashMap.class, pd);
+ delegates.put(javax.swing.UIDefaults.class, pd);
+
+ // TODO: These classes need to be implemented first
+ //delegates.put(java.security.AuthProvider.class, pd);
+ //delegates.put(java.util.concurrent.ConcurrentHashMap.class, pd);
+ //delegates.put(java.util.EnumMap.class, pd);
+ //delegates.put(javax.management.openmbean.TabularDataSupport.class, pd);
+
+ defaultPersistenceDelegate = new DefaultPersistenceDelegate();
+ delegates.put(Object.class, defaultPersistenceDelegate);
+
+ // Creates a PersistenceDelegate implementation which is
+ // returned for 'null'. In practice this instance is
+ // not used in any way and is just here to be compatible
+ // with the reference implementation which returns a
+ // similar instance when calling getPersistenceDelegate(null) .
+ fakePersistenceDelegate = new PersistenceDelegate()
+ {
+ protected Expression instantiate(Object o, Encoder e)
+ {
+ return null;
+ }
+ };
+
+ }
+ }
+
+ protected void writeObject(Object o)
+ {
+ // 'null' has no PersistenceDelegate and will not
+ // create an Expression which has to be cloned.
+ // However subclasses should be aware that writeObject
+ // may be called with a 'null' argument and should
+ // write the proper representation of it.
+ if (o == null)
+ return;
+
+ PersistenceDelegate pd = getPersistenceDelegate(o.getClass());
+
+ accessCounter++;
+ pd.writeObject(o, this);
+ accessCounter--;
+
+ }
+
+ /**
+ * Sets the {@link ExceptionListener} instance to be used for reporting
+ * recorable exceptions in the instantiation and initialization sequence. If
+ * the argument is <code>null</code> a default instance will be used that
+ * prints the thrown exception to <code>System.err</code>.
+ */
+ public void setExceptionListener(ExceptionListener listener)
+ {
+ exceptionListener = (listener != null) ? listener : new ExceptionListener()
+ {
+ public void exceptionThrown(Exception e)
+ {
+ System.err.println("exception thrown: " + e);
+ e.printStackTrace();
+ }
+ };
+ }
+
+ /**
+ * Returns the currently active {@link ExceptionListener} instance.
+ */
+ public ExceptionListener getExceptionListener()
+ {
+ return exceptionListener;
+ }
+
+ public PersistenceDelegate getPersistenceDelegate(Class type)
+ {
+ // This is not specified but the JDK behaves like this.
+ if (type == null)
+ return fakePersistenceDelegate;
+
+ // Treats all array classes in the same way and assigns
+ // them a shared PersistenceDelegate implementation tailored
+ // for array instantation and initialization.
+ if (type.isArray())
+ return (PersistenceDelegate) delegates.get(Object[].class);
+
+ PersistenceDelegate pd = (PersistenceDelegate) delegates.get(type);
+
+ return (pd != null) ? pd : (PersistenceDelegate) defaultPersistenceDelegate;
+ }
+
+ /**
+ * Sets the {@link PersistenceDelegate} instance for the given class.
+ * <p>
+ * Note: Throws a <code>NullPointerException</code> if the argument is
+ * <code>null</code>.
+ * </p>
+ * <p>
+ * Note: Silently ignores PersistenceDelegates for Array types and primitive
+ * wrapper classes.
+ * </p>
+ * <p>
+ * Note: Although this method is not declared <code>static</code> changes to
+ * the {@link PersistenceDelegate}s affect <strong>all</strong>
+ * {@link Encoder} instances. <strong>In this implementation</strong> the
+ * access is thread safe.
+ * </p>
+ */
+ public void setPersistenceDelegate(Class type, PersistenceDelegate delegate)
+ {
+ // If the argument is null this will cause a NullPointerException
+ // which is expected behavior.
+
+ // This makes custom PDs for array, primitive types and their wrappers
+ // impossible but this is how the JDK behaves.
+ if (type.isArray() || type.isPrimitive() || type == Boolean.class
+ || type == Byte.class || type == Short.class || type == Integer.class
+ || type == Long.class || type == Float.class || type == Double.class)
+ return;
+
+ synchronized (delegates)
+ {
+ delegates.put(type, delegate);
+ }
+
+ }
+
+ public Object remove(Object oldInstance)
+ {
+ return candidates.remove(oldInstance);
+ }
+
+ /**
+ * Returns the replacement object which has been created by the encoder during
+ * the instantiation sequence or <code>null</code> if the object has not
+ * been processed yet.
+ * <p>
+ * Note: The <code>String</code> class acts as an endpoint for the
+ * inherently recursive algorithm of the {@link Encoder}. Therefore instances
+ * of <code>String</code> will always be returned by this method. In other
+ * words the assertion: <code>
+ * assert (anyEncoder.get(anyString) == anyString)
+ * </code<
+ * will always hold.</p>
+ *
+ * <p>Note: If <code>null</code> is requested, the result will
+ * always be <code>null</code>.</p>
+ */
+ public Object get(Object oldInstance)
+ {
+ // String instances are handled in a special way.
+ // No one knows why this is not officially specified
+ // because this is a rather important design decision.
+ return (oldInstance == null) ? null :
+ (oldInstance.getClass() == String.class) ?
+ oldInstance : candidates.get(oldInstance);
+ }
+
+ /**
+ * <p>
+ * Note: If you call this method not from within an object instantiation and
+ * initialization sequence it will be silently ignored.
+ * </p>
+ */
+ public void writeStatement(Statement stmt)
+ {
+ // Silently ignore out of bounds calls.
+ if (accessCounter <= 0)
+ return;
+
+ Object target = stmt.getTarget();
+
+ Object newTarget = get(target);
+ if (newTarget == null)
+ {
+ writeObject(target);
+ newTarget = get(target);
+ }
+
+ Object[] args = stmt.getArguments();
+ Object[] newArgs = new Object[args.length];
+
+ for (int i = 0; i < args.length; i++)
+ {
+ newArgs[i] = get(args[i]);
+ if (newArgs[i] == null || isImmutableType(args[i].getClass()))
+ {
+ writeObject(args[i]);
+ newArgs[i] = get(args[i]);
+ }
+ }
+
+ Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs);
+
+ try
+ {
+ newStmt.execute();
+ }
+ catch (Exception e)
+ {
+ exceptionListener.exceptionThrown(e);
+ }
+
+ }
+
+ /**
+ * <p>
+ * Note: If you call this method not from within an object instantiation and
+ * initialization sequence it will be silently ignored.
+ * </p>
+ */
+ public void writeExpression(Expression expr)
+ {
+ // Silently ignore out of bounds calls.
+ if (accessCounter <= 0)
+ return;
+
+ Object target = expr.getTarget();
+ Object value = null;
+ Object newValue = null;
+
+ try
+ {
+ value = expr.getValue();
+ }
+ catch (Exception e)
+ {
+ exceptionListener.exceptionThrown(e);
+ return;
+ }
+
+
+ newValue = get(value);
+
+ if (newValue == null)
+ {
+ Object newTarget = get(target);
+ if (newTarget == null)
+ {
+ writeObject(target);
+ newTarget = get(target);
+
+ // May happen if exception was thrown.
+ if (newTarget == null)
+ {
+ return;
+ }
+ }
+
+ Object[] args = expr.getArguments();
+ Object[] newArgs = new Object[args.length];
+
+ for (int i = 0; i < args.length; i++)
+ {
+ newArgs[i] = get(args[i]);
+ if (newArgs[i] == null || isImmutableType(args[i].getClass()))
+ {
+ writeObject(args[i]);
+ newArgs[i] = get(args[i]);
+ }
+ }
+
+ Expression newExpr = new Expression(newTarget, expr.getMethodName(),
+ newArgs);
+
+ // Fakes the result of Class.forName(<primitiveType>) to make it possible
+ // to hand such a type to the encoding process.
+ if (value instanceof Class && ((Class) value).isPrimitive())
+ newExpr.setValue(value);
+
+ // Instantiates the new object.
+ try
+ {
+ newValue = newExpr.getValue();
+
+ candidates.put(value, newValue);
+ }
+ catch (Exception e)
+ {
+ exceptionListener.exceptionThrown(e);
+
+ return;
+ }
+
+ writeObject(value);
+
+ }
+ else if(value.getClass() == String.class || value.getClass() == Class.class)
+ {
+ writeObject(value);
+ }
+
+ }
+
+ /** Returns whether the given class is an immutable
+ * type which has to be handled differently when serializing it.
+ *
+ * <p>Immutable objects always have to be instantiated instead of
+ * modifying an existing instance.</p>
+ *
+ * @param type The class to test.
+ * @return Whether the first argument is an immutable type.
+ */
+ boolean isImmutableType(Class type)
+ {
+ return type == String.class || type == Class.class
+ || type == Integer.class || type == Boolean.class
+ || type == Byte.class || type == Short.class
+ || type == Long.class || type == Float.class
+ || type == Double.class;
+ }
+
+ /** Sets the stream candidate for a given object.
+ *
+ * @param oldObject The object given to the encoder.
+ * @param newObject The object the encoder generated.
+ */
+ void putCandidate(Object oldObject, Object newObject)
+ {
+ candidates.put(oldObject, newObject);
+ }
+
+}
diff --git a/libjava/classpath/java/beans/EventSetDescriptor.java b/libjava/classpath/java/beans/EventSetDescriptor.java
index 8624e643476..381a453031e 100644
--- a/libjava/classpath/java/beans/EventSetDescriptor.java
+++ b/libjava/classpath/java/beans/EventSetDescriptor.java
@@ -1,39 +1,39 @@
/* java.beans.EventSetDescriptor
- Copyright (C) 1998 Free Software Foundation, Inc.
+ Copyright (C) 1998, 2006 Free Software Foundation, Inc.
-This file is part of GNU Classpath.
+ This file is part of GNU Classpath.
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+ GNU Classpath is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. */
+ GNU Classpath is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Classpath; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library. Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module. An independent module is a module which is not derived from
+ or based on this library. If you modify this library, you may extend
+ this exception to your version of the library, but 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;
@@ -45,398 +45,719 @@ 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 &lt;listenerMethodName&gt;(&lt;eventSetName&gt;Event)</CODE>
- ** (where <CODE>&lt;eventSetName&gt;</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&lt;eventSetName&gt;Listener(&lt;listenerType&gt;)</CODE> and
- ** <CODE>void remove&lt;eventSetName&gt;Listener(&lt;listenerType&gt;)</CODE> in
- ** in class <CODE>eventSourceClass</CODE>, where
- ** <CODE>&lt;eventSetName&gt;</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);
- }
- }
+ * EventSetDescriptor describes the hookup between an event source class and
+ * an event listener class.
+ *
+ * <p>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>
+ *
+ * <p>
+ * The methods have these constraints on them:
+ * <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>
+ *
+ * <p>
+ * A final constraint is that event listener classes must extend from
+ * EventListener.
+ * </p>
+ *
+ * <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>
+ *
+ * <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
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.1
+ */
+
+public class EventSetDescriptor extends FeatureDescriptor
+{
+ private Method addListenerMethod;
+
+ private Method removeListenerMethod;
+
+ private Class listenerType;
+
+ private MethodDescriptor[] listenerMethodDescriptors;
+
+ private Method[] listenerMethods;
+
+ private Method getListenerMethod;
+
+ private boolean unicast;
+
+ private boolean inDefaultEventSet = true;
+
+ /**
+ * Creates a new <code>EventSetDescriptor</code<.
+ *
+ * <p>
+ * 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
+ * &lt;listenerMethodName&gt;(&lt;eventSetName&gt;Event)</code> (where
+ * <code>&lt;eventSetName&gt;</code> has its first character capitalized
+ * by the constructor and the Event is a descendant of
+ * {@link java.util.EventObject}) 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: <code>java.util.MouseEvent</code> vs.
+ * <code>my.very.own.MouseEvent</code>). 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&lt;eventSetName&gt;Listener(&lt;listenerType&gt;)</code> and
+ * <code>void remove&lt;eventSetName&gt;Listener(&lt;listenerType&gt;)</code>
+ * in in class <code>eventSourceClass</code>, where
+ * <code>&lt;eventSetName&gt;</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.");
+ }
+ }
+
+ /**
+ * Creates a new <code>EventSetDescriptor</code>.
+ *
+ * <p>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.");
+ }
+ }
+
+ /**
+ * Creates a new <code>EventSetDescriptor</code>.
+ *
+ * <p>
+ * This variant 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>
+ * <p>
+ * A valid GetListener method is public, flags no exceptions and has one
+ * argument which is of type <code>Class</code>
+ * {@link java.awt.Component#getListeners(Class)} is such a method.
+ * </p>
+ * <p>
+ * Note: The validity of the return value of the GetListener method is not
+ * checked.
+ * </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.
+ * @param getListenerMethodName
+ * Name of a method which returns the array of listeners.
+ * @exception IntrospectionException
+ * if listenerType is not an EventListener or if methods are not
+ * found or are invalid.
+ * @since 1.4
+ */
+ public EventSetDescriptor(Class eventSourceClass, String eventSetName,
+ Class listenerType, String[] listenerMethodNames,
+ String addListenerMethodName,
+ String removeListenerMethodName,
+ String getListenerMethodName)
+ throws IntrospectionException
+ {
+ this(eventSourceClass, eventSetName, listenerType, listenerMethodNames,
+ addListenerMethodName, removeListenerMethodName);
+
+ Method newGetListenerMethod = null;
+
+ try
+ {
+ newGetListenerMethod
+ = eventSourceClass.getMethod(getListenerMethodName,
+ new Class[] { Class.class });
+ }
+ catch (NoSuchMethodException nsme)
+ {
+ throw (IntrospectionException)
+ new IntrospectionException("No method named " + getListenerMethodName
+ + " in class " + listenerType
+ + " which can be used as"
+ + " getListenerMethod.").initCause(nsme);
+ }
+
+ // Note: This does not check the return value (which
+ // should be EventListener[]) but the JDK does not either.
+
+ getListenerMethod = newGetListenerMethod;
+
+ }
+
+ /**
+ * Creates a new <code>EventSetDescriptor.</code>
+ *
+ * <p>
+ * This variant 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>
+ * <p>
+ * A valid GetListener method is public, flags no exceptions and has one
+ * argument which is of type <code>Class</code>
+ * {@link java.awt.Component#getListeners(Class)} is such a method.
+ * </p>
+ * <p>
+ * Note: The validity of the return value of the GetListener method is not
+ * checked.
+ * </p>
+ *
+ * @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.
+ * @param getListenerMethod
+ * The method which returns an array of the listeners.
+ * @exception IntrospectionException
+ * if the listenerType is not an EventListener, or any of the
+ * methods are invalid.
+ * @since 1.4
+ */
+ public EventSetDescriptor(String eventSetName, Class listenerType,
+ Method[] listenerMethods, Method addListenerMethod,
+ Method removeListenerMethod,
+ Method getListenerMethod)
+ throws IntrospectionException
+ {
+ this(eventSetName, listenerType, listenerMethods, addListenerMethod,
+ removeListenerMethod);
+
+ // Do no checks if the getListenerMethod is null.
+ if (getListenerMethod.getParameterTypes().length != 1
+ || getListenerMethod.getParameterTypes()[0] != Class.class
+ || getListenerMethod.getExceptionTypes().length > 0
+ || !Modifier.isPublic(getListenerMethod.getModifiers()))
+ throw new IntrospectionException("GetListener method is invalid.");
+
+ // Note: This does not check the return value (which
+ // should be EventListener[]) but the JDK does not either.
+
+ this.getListenerMethod = getListenerMethod;
+ }
+
+ /**
+ * Creates a new <code>EventSetDescriptor</code>.
+ *
+ * <p>This form of constructor allows you to explicitly say which methods
+ * do what, and no reflection is done by the <code>EventSetDescriptor</code>.
+ * 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.");
+ }
+ }
+
+ /** Creates a new <code>EventSetDescriptor</code>.
+ *
+ * <p>This form of constructor allows you to explicitly say which methods do
+ * what, and no reflection is done by the <code>EventSetDescriptor</code>.
+ * 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.");
+ }
+ }
+
+ /** Returns the class that contains the event firing methods.
+ */
+ public Class getListenerType()
+ {
+ return listenerType;
+ }
+
+ /** Returns the event firing methods.
+ */
+ public Method[] getListenerMethods()
+ {
+ return listenerMethods;
+ }
+
+ /** Returns the event firing methods as {@link MethodDescriptor}.
+ */
+ 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;
+ }
+
+ /** Returns the add listener method.
+ */
+ public Method getAddListenerMethod()
+ {
+ return addListenerMethod;
+ }
+
+ /* Returns the remove listener method.
+ */
+ public Method getRemoveListenerMethod()
+ {
+ return removeListenerMethod;
+ }
+
+ /**
+ * Returns the method that retrieves the listeners or <code>null</code> if
+ * it does not exist.
+ */
+ public Method getGetListenerMethod()
+ {
+ return getListenerMethod;
+ }
+
+ /** Sets 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;
+ }
+
+ /** Returns whether or not multiple listeners may be added.
+ * (Defaults to false.)
+ */
+ public boolean isUnicast()
+ {
+ return unicast;
+ }
+
+ /** Sets 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;
+ }
+
+ /** Returns 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/Expression.java b/libjava/classpath/java/beans/Expression.java
index d92cb7284aa..b327864d95f 100644
--- a/libjava/classpath/java/beans/Expression.java
+++ b/libjava/classpath/java/beans/Expression.java
@@ -35,16 +35,19 @@ this exception to your version of the library, but 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.
- *
+ * <p>An Expression captures the execution of an object method
+ * that returns a value.</p>
+ *
+ * <p>It stores an object, the method to call, and the arguments to pass to
+ * the method.</p>
+ *
+ * <p>While this class can generally be used to describe method calls it is
+ * part of the XML serialization API.</p>
+ *
+ * @author Robert Schuster (robertschuster@fsfe.org)
* @since 1.4
*/
public class Expression extends Statement
@@ -53,38 +56,40 @@ public class Expression extends Statement
// yet;
private static final Object UNSET = new Object();
- // The value to return. This is equal to unset until getValue is called.
+ // 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.
+ * 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)
+ Object[] arguments)
{
super(target, methodName, arguments);
this.value = value;
}
/**
- * Constructor
- *
- * Constructs an Expression representing the invocation of
+ * 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.
+ *
+ * @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)
{
@@ -93,15 +98,14 @@ public class Expression extends Statement
}
/**
- * 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
+ * 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
+ * @exception Exception
+ * if an error occurs
*/
public Object getValue() throws Exception
{
@@ -112,14 +116,15 @@ public class Expression extends Statement
/**
* Set the cached value to be returned by getValue()
- *
- * @param value the value to cache and return.
+ *
+ * @param value
+ * the value to cache and return.
*/
public void setValue(Object value)
{
this.value = value;
}
-
+
/**
* Return a string representation of this expression.
*/
@@ -127,7 +132,7 @@ public class Expression extends Statement
{
String result = super.toString();
if (value != UNSET)
- return value.getClass().getName() + " " + result;
+ return value.getClass().getName() + "=" + result;
return result;
}
}
diff --git a/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java b/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java
new file mode 100644
index 00000000000..1a7306d14db
--- /dev/null
+++ b/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java
@@ -0,0 +1,81 @@
+/* Indexed property change event
+ 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. */
+
+
+package java.beans;
+
+/**
+ * This is like a PropertyChangeEvent, but also carries with it the
+ * index of the property which changed.
+ * @author Tom Tromey (tromey@redhat.com)
+ * @since 1.5
+ */
+public class IndexedPropertyChangeEvent extends PropertyChangeEvent
+{
+ private static final long serialVersionUID = -320227448495806870L;
+
+ /**
+ * Index of the item that was changed.
+ */
+ private int index;
+
+ /**
+ * Create a new IndexedPropertyChangeEvent.
+ * @param source the Bean containing the property
+ * @param name the property's name
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @param index the index of the element in the property which changed
+ * @throws IllegalArgumentException if source is null
+ */
+ public IndexedPropertyChangeEvent(Object source, String name,
+ Object oldValue, Object newValue,
+ int index)
+ {
+ super(source, name, oldValue, newValue);
+ this.index = index;
+ }
+
+ /**
+ * Return the index of the changed property.
+ * @return the index
+ */
+ public int getIndex()
+ {
+ return index;
+ }
+}
diff --git a/libjava/classpath/java/beans/Introspector.java b/libjava/classpath/java/beans/Introspector.java
index 02781b46e0d..23c3cde5e3d 100644
--- a/libjava/classpath/java/beans/Introspector.java
+++ b/libjava/classpath/java/beans/Introspector.java
@@ -211,6 +211,82 @@ public class Introspector {
return cachedInfo;
}
}
+
+ /**
+ * Returns a {@BeanInfo} instance for the given Bean class where a flag
+ * controls the usage of explicit BeanInfo class to retrieve that
+ * information.
+ *
+ * <p>You have three options:</p>
+ * <p>With {@link #USE_ALL_BEANINFO} the result is the same as
+ * {@link #getBeanInfo(Class)}.</p>
+ *
+ * <p>Calling the method with <code>flag</code> set to
+ * {@link #IGNORE_IMMEDIATE_BEANINFO} will let it use all
+ * explicit BeanInfo classes for the beans superclasses
+ * but not for the bean class itself. Furthermore eventset,
+ * property and method information is retrieved by introspection
+ * if the explicit <code>BeanInfos</code> did not provide such data
+ * (ie. return <code>null</code> on {@link BeanInfo.getMethodDescriptors},
+ * {@link BeanInfo.getEventSetDescriptors} and
+ * {@link BeanInfo.getPropertyDescriptors}.)
+ * </p>
+ *
+ * <p>When the method is called with <code>flag</code< set to
+ * {@link #IGNORE_ALL_BEANINFO} all the bean data is retrieved
+ * by inspecting the class.</p>
+ *
+ * <p>Note: Any unknown value for <code>flag</code> is interpreted
+ * as {@link #IGNORE_ALL_BEANINFO}</p>.
+ *
+ * @param beanClass The class whose BeanInfo should be returned.
+ * @param flag Controls the usage of explicit <code>BeanInfo</code> classes.
+ * @return A BeanInfo object describing the class.
+ * @throws IntrospectionException If something goes wrong while retrieving
+ * the bean data.
+ */
+ public static BeanInfo getBeanInfo(Class beanClass, int flag)
+ throws IntrospectionException
+ {
+ IntrospectionIncubator ii;
+ BeanInfoEmbryo infoEmbryo;
+
+ switch(flag)
+ {
+ case USE_ALL_BEANINFO:
+ return getBeanInfo(beanClass);
+ case IGNORE_IMMEDIATE_BEANINFO:
+ Class superclass = beanClass.getSuperclass();
+ ExplicitInfo explicit = new ExplicitInfo(superclass, null);
+
+ ii = new IntrospectionIncubator();
+ if (explicit.explicitEventSetDescriptors != null)
+ ii.setEventStopClass(superclass);
+
+ if (explicit.explicitMethodDescriptors != null)
+ ii.setMethodStopClass(superclass);
+
+ if (explicit.explicitPropertyDescriptors != null)
+ ii.setPropertyStopClass(superclass);
+
+ ii.addMethods(beanClass.getMethods());
+
+ infoEmbryo = ii.getBeanInfoEmbryo();
+ merge(infoEmbryo, explicit);
+
+ infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null));
+
+ return infoEmbryo.getBeanInfo();
+ case IGNORE_ALL_BEANINFO:
+ default:
+ ii = new IntrospectionIncubator();
+ ii.addMethods(beanClass.getMethods());
+ infoEmbryo = ii.getBeanInfoEmbryo();
+ infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null));
+
+ return infoEmbryo.getBeanInfo();
+ }
+ }
/**
* Flush all of the Introspector's internal caches.
@@ -244,6 +320,69 @@ public class Introspector {
}
}
+ /** Adds all explicity given bean info data to the introspected
+ * data.
+ *
+ * @param infoEmbryo Bean info data retrieved by introspection.
+ * @param explicit Bean info data retrieved by BeanInfo classes.
+ */
+ private static void merge(BeanInfoEmbryo infoEmbryo, ExplicitInfo explicit)
+ {
+ PropertyDescriptor[] p = explicit.explicitPropertyDescriptors;
+ if(p!=null)
+ {
+ for(int i=0;i<p.length;i++)
+ {
+ if(!infoEmbryo.hasProperty(p[i]))
+ {
+ infoEmbryo.addProperty(p[i]);
+ }
+ }
+
+ // -1 should be used to denote a missing default property but
+ // for robustness reasons any value below zero is discarded.
+ // Not doing so would let Classpath fail where the JDK succeeds.
+ if(explicit.defaultProperty > -1)
+ {
+ infoEmbryo.setDefaultPropertyName(p[explicit.defaultProperty].getName());
+ }
+ }
+ EventSetDescriptor[] e = explicit.explicitEventSetDescriptors;
+ if(e!=null)
+ {
+ for(int i=0;i<e.length;i++)
+ {
+ if(!infoEmbryo.hasEvent(e[i]))
+ {
+ infoEmbryo.addEvent(e[i]);
+ }
+ }
+
+ // -1 should be used to denote a missing default event but
+ // for robustness reasons any value below zero is discarded.
+ // Not doing so would let Classpath fail where the JDK succeeds.
+ if(explicit.defaultEvent > -1)
+ {
+ infoEmbryo.setDefaultEventName(e[explicit.defaultEvent].getName());
+ }
+ }
+ MethodDescriptor[] m = explicit.explicitMethodDescriptors;
+ if(m!=null)
+ {
+ for(int i=0;i<m.length;i++)
+ {
+ if(!infoEmbryo.hasMethod(m[i]))
+ {
+ infoEmbryo.addMethod(m[i]);
+ }
+ }
+ }
+
+ infoEmbryo.setAdditionalBeanInfo(explicit.explicitBeanInfo);
+ infoEmbryo.setIcons(explicit.im);
+
+ }
+
/**
* Get the BeanInfo for class <CODE>beanClass</CODE>,
* first by looking for explicit information, next by
@@ -267,62 +406,19 @@ public class Introspector {
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);
+ merge(currentInfo, explicit);
+ // 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));
return currentInfo.getBeanInfo();
}
diff --git a/libjava/classpath/java/beans/PersistenceDelegate.java b/libjava/classpath/java/beans/PersistenceDelegate.java
new file mode 100644
index 00000000000..b33cbcbed06
--- /dev/null
+++ b/libjava/classpath/java/beans/PersistenceDelegate.java
@@ -0,0 +1,91 @@
+/* java.beans.PersistenceDelegate
+ 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. */
+
+package java.beans;
+
+/** <p>A <code>PersistenceDelegate</code> describes how a another object
+ * has to constructed and transformed in order to create a complete
+ * replicate.</p>
+ *
+ * <p>For custom classes you will need to implement
+ * <code>PersistenceDelegate</code> in a way that is suitable for them.
+ * To make use of the implementation you have to register it with an
+ * {@link Encoder} using the {Encoder#setPersistenceDelegate} method.</p>
+ *
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.4
+ */
+public abstract class PersistenceDelegate
+{
+
+ protected void initialize(Class type, Object oldInstance, Object newInstance,
+ Encoder out)
+ {
+ if (type != Object.class)
+ {
+ type = type.getSuperclass();
+
+ PersistenceDelegate pd = out.getPersistenceDelegate(
+ oldInstance.getClass().getSuperclass());
+
+ pd.initialize(type, oldInstance, newInstance, out);
+ }
+ }
+
+ public void writeObject(Object oldInstance, Encoder out)
+ {
+ Object streamCandidate = out.get(oldInstance);
+
+ if (mutatesTo(oldInstance, streamCandidate))
+ {
+ initialize(oldInstance.getClass(), oldInstance, streamCandidate, out);
+ }
+ else
+ {
+ out.remove(oldInstance);
+ out.writeExpression(instantiate(oldInstance, out));
+ }
+ }
+
+ protected boolean mutatesTo(Object oldInstance, Object newInstance)
+ {
+ return (newInstance != null)
+ && oldInstance.getClass() == newInstance.getClass();
+ }
+
+ protected abstract Expression instantiate(Object oldInstance, Encoder out);
+}
diff --git a/libjava/classpath/java/beans/PropertyChangeSupport.java b/libjava/classpath/java/beans/PropertyChangeSupport.java
index a0e64af4d29..991390b5631 100644
--- a/libjava/classpath/java/beans/PropertyChangeSupport.java
+++ b/libjava/classpath/java/beans/PropertyChangeSupport.java
@@ -397,6 +397,58 @@ public class PropertyChangeSupport implements Serializable
}
/**
+ * Fire an indexed property change event. This will only fire
+ * an event if the old and new values are not equal and not null.
+ * @param name the name of the property which changed
+ * @param index the index of the property which changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @since 1.5
+ */
+ public void fireIndexedPropertyChange(String name, int index,
+ Object oldValue, Object newValue)
+ {
+ // Argument checking is done in firePropertyChange(PropertyChangeEvent) .
+ firePropertyChange(new IndexedPropertyChangeEvent(source, name,
+ oldValue, newValue,
+ index));
+ }
+
+ /**
+ * Fire an indexed property change event. This will only fire
+ * an event if the old and new values are not equal.
+ * @param name the name of the property which changed
+ * @param index the index of the property which changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @since 1.5
+ */
+ public void fireIndexedPropertyChange(String name, int index,
+ int oldValue, int newValue)
+ {
+ if (oldValue != newValue)
+ fireIndexedPropertyChange(name, index, Integer.valueOf(oldValue),
+ Integer.valueOf(newValue));
+ }
+
+ /**
+ * Fire an indexed property change event. This will only fire
+ * an event if the old and new values are not equal.
+ * @param name the name of the property which changed
+ * @param index the index of the property which changed
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ * @since 1.5
+ */
+ public void fireIndexedPropertyChange(String name, int index,
+ boolean oldValue, boolean newValue)
+ {
+ if (oldValue != newValue)
+ fireIndexedPropertyChange(name, index, Boolean.valueOf(oldValue),
+ Boolean.valueOf(newValue));
+ }
+
+ /**
* 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.
diff --git a/libjava/classpath/java/beans/Statement.java b/libjava/classpath/java/beans/Statement.java
index 8e916a286be..62a5ad7b6f8 100644
--- a/libjava/classpath/java/beans/Statement.java
+++ b/libjava/classpath/java/beans/Statement.java
@@ -1,4 +1,4 @@
-/* java.beans.Statement
+/* Statement.java
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -42,32 +42,26 @@ import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.WeakHashMap;
-
/**
- * class Statement
- *
- * A Statement captures the execution of an object method. It stores
+ * <p>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.
+ * provided arguments.</p>
*
+ * @author Jerry Quinn (jlquinn@optonline.net)
+ * @author Robert Schuster (robertschuster@fsfe.org)
* @since 1.4
*/
public class Statement
{
- /** Nested map for the relation between a class, its instances and their
- * names.
- */
- private static HashMap classMaps = new HashMap();
-
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.
+ /**
+ * 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;
@@ -87,76 +81,44 @@ public class Statement
this.target = target;
this.methodName = methodName;
this.arguments = (arguments != null) ? arguments : new Object[0];
- storeTargetName(target);
- }
-
- /** Creates a name for the target instance or does nothing if the object's
- * name is already known. This makes sure that there *is* a name for every
- * target instance.
- */
- private static synchronized void storeTargetName(Object obj)
- {
- Class klass = obj.getClass();
- WeakHashMap names = (WeakHashMap) classMaps.get(klass);
-
- if ( names == null )
- {
- names = new WeakHashMap();
-
- names.put(obj,
- ( klass == String.class ? ("\"" + obj + "\"") :
- (klass.getName() + names.size()) ));
-
- classMaps.put(klass, names);
-
- return;
- }
-
- String targetName = (String) names.get(obj);
- if ( targetName == null )
- {
- names.put(obj,
- ( klass == String.class ? ("\"" + obj + "\"") :
- (klass.getName() + names.size()) ));
- }
-
- // Nothing to do. The given object was already stored.
}
/**
* Execute the statement.
*
- * Finds the specified method in the target object and calls it with
- * the arguments given in the constructor.
+ * <p>Finds the specified method in the target object and calls it with
+ * the arguments given in the constructor.</p>
*
- * The most specific method according to the JLS(15.11) is used when
- * there are multiple methods with the same name.
+ * <p>The most specific method according to the JLS(15.11) is used when
+ * there are multiple methods with the same name.</p>
*
- * Execute performs some special handling for methods and
+ * <p>Execute performs some special handling for methods and
* parameters:
+ * <ul>
+ * <li>Static methods can be executed by providing the class as a
+ * target.</li>
*
- * Static methods can be executed by providing the class as a
- * target.
- *
- * The method name new is reserved to call the constructor
+ * <li>The method name new is reserved to call the constructor
* new() will construct an object and return it. Not useful unless
- * an expression :-)
+ * an expression :-)</li>
*
- * If the target is an array, get and set as defined in
+ * <li>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.
+ * methods of the same name in java.lang.reflect.Array.</li>
*
- * The native datatype wrappers Boolean, Byte, Character, Double,
+ * <li>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 wrapper type as an argument.</li>
+ * </ul>
+ * </p>
*
- * The Sun spec doesn't deal with overloading between int and
+ * <p>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.
+ * declared in the source file.</p>
*
* @throws Exception if an exception occurs while locating or
* invoking the method.
@@ -178,8 +140,10 @@ public class Statement
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.
+ /** Given a wrapper class, return the native class for it.
+ * <p>For example, if <code>c</code> is <code>Integer</code>,
+ * <code>Integer.TYPE</code> is returned.</p>
+ */
private Class unwrap(Class c)
{
for (int i = 0; i < wrappers.length; i++)
@@ -188,13 +152,22 @@ public class Statement
return null;
}
- // Return true if all args can be assigned to params, false
- // otherwise. Arrays are guaranteed to be the same length.
+ /** Returns <code>true</code> if all args can be assigned to
+ * <code>params</code>, <code>false</code> otherwise.
+ *
+ * <p>Arrays are guaranteed to be the same length.</p>
+ */
private boolean compatible(Class[] params, Class[] args)
{
for (int i = 0; i < params.length; i++)
{
- // Treat Integer like int if appropriate
+ // Argument types are derived from argument values. If one of them was
+ // null then we cannot deduce its type. However null can be assigned to
+ // any type.
+ if (args[i] == null)
+ continue;
+
+ // Treat Integer like int if appropriate
Class nativeType = unwrap(args[i]);
if (nativeType != null && params[i].isPrimitive()
&& params[i].isAssignableFrom(nativeType))
@@ -208,14 +181,15 @@ public class Statement
}
/**
- * 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.
+ * Returns <code>true</code> if the method arguments in first are
+ * more specific than the method arguments in second, i.e. all
+ * arguments in <code>first</code> can be assigned to those in
+ * <code>second</code>.
*
- * A method is more specific if all parameters can also be fed to
+ * <p>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.
+ * specific one.</p>
*
* @param first a <code>Class[]</code> value
* @param second a <code>Class[]</code> value
@@ -238,8 +212,11 @@ public class Statement
? (Class) target : target.getClass();
Object args[] = (arguments == null) ? new Object[0] : arguments;
Class argTypes[] = new Class[args.length];
+
+ // Retrieve type or use null if the argument is null. The null argument
+ // type is later used in compatible().
for (int i = 0; i < args.length; i++)
- argTypes[i] = args[i].getClass();
+ argTypes[i] = (args[i] != null) ? args[i].getClass() : null;
if (target.getClass().isArray())
{
@@ -333,7 +310,29 @@ public class Statement
}
if (method == null)
throw new NoSuchMethodException("No matching method for statement " + toString());
+
+ // If we were calling Class.forName(String) we intercept and call the
+ // forName-variant that allows a ClassLoader argument. We take the
+ // system classloader (aka application classloader) here to make sure
+ // that application defined classes can be resolved. If we would not
+ // do that the Class.forName implementation would use the class loader
+ // of java.beans.Statement which is <null> and cannot resolve application
+ // defined classes.
+ if (method.equals(
+ Class.class.getMethod("forName", new Class[] { String.class })))
+ return Class.forName(
+ (String) args[0], true, ClassLoader.getSystemClassLoader());
+
+ try {
return method.invoke(target, args);
+ } catch(IllegalArgumentException iae){
+ System.err.println("method: " + method);
+
+ for(int i=0;i<args.length;i++){
+ System.err.println("args[" + i + "]: " + args[i]);
+ }
+ throw iae;
+ }
}
@@ -352,9 +351,13 @@ public class Statement
{
StringBuffer result = new StringBuffer();
- Class klass = target.getClass();
+ String targetName = target.getClass().getName();
+ if ( targetName.startsWith("java"))
+ {
+ targetName = targetName.substring(targetName.lastIndexOf('.') + 1);
+ }
- result.append( ((WeakHashMap) classMaps.get(klass)).get(target));
+ result.append(targetName);
result.append(".");
result.append(methodName);
result.append("(");
@@ -363,11 +366,15 @@ public class Statement
for (int i = 0; i < arguments.length; i++)
{
result.append(sep);
- result.append(arguments[i].getClass().getName());
+ result.append(
+ ( arguments[i] == null ) ? "null" :
+ ( arguments[i] instanceof String ) ? "\"" + arguments[i] + "\"" :
+ arguments[i].getClass().getName());
sep = ", ";
}
result.append(")");
return result.toString();
}
+
}
diff --git a/libjava/classpath/java/beans/XMLEncoder.java b/libjava/classpath/java/beans/XMLEncoder.java
new file mode 100644
index 00000000000..f9cbe63963d
--- /dev/null
+++ b/libjava/classpath/java/beans/XMLEncoder.java
@@ -0,0 +1,265 @@
+/* XMLEncoder.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.beans;
+
+import gnu.java.beans.encoder.ScanEngine;
+
+import java.io.OutputStream;
+
+/**
+ * This class uses the {@link PersistenceDelegate} and {@link Encoder}
+ * infrastructure to generate an XML representation of the objects it
+ * serializes.
+ *
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.4
+ */
+public class XMLEncoder extends Encoder
+{
+ Object owner;
+
+ Exception exception;
+
+ ScanEngine scanEngine;
+
+ private int accessCounter = 0;
+
+ public XMLEncoder(OutputStream os)
+ {
+ scanEngine = new ScanEngine(os);
+ }
+
+ public void close()
+ {
+ if (scanEngine != null)
+ {
+ scanEngine.close();
+ scanEngine = null;
+ }
+ }
+
+ public void flush()
+ {
+ scanEngine.flush();
+ }
+
+ public void writeExpression(Expression expr)
+ {
+ // Implementation note: Why is this method overwritten and nearly exactly
+ // reimplemented as in Encoder?
+ // The Encoder class can (and should be) subclassed by users outside of the
+ // java.beans package. While I have doubts that this is possible from an
+ // API design point of view I tried to replicate the Encoder's behavior
+ // in the JDK as exactly as possible. This strictness however made it
+ // extremely complicated to implement the XMLEncoder's backend. Therefore
+ // I decided to copy the Encoder's implementation and make all changes
+ // I needed for a succesfull operation of XMLEncoder.
+ //
+ // The same is true for the writeStatement method.
+
+ // Silently ignore out of bounds calls.
+ if (accessCounter <= 0)
+ return;
+
+ scanEngine.writeExpression(expr);
+
+
+ Object target = expr.getTarget();
+ Object value = null;
+ Object newValue = null;
+
+ try
+ {
+ value = expr.getValue();
+ }
+ catch (Exception e)
+ {
+ getExceptionListener().exceptionThrown(e);
+ return;
+ }
+
+
+ newValue = get(value);
+
+ if (newValue == null)
+ {
+ Object newTarget = get(target);
+ if (newTarget == null)
+ {
+ writeObject(target);
+ newTarget = get(target);
+
+ // May happen if exception was thrown.
+ if (newTarget == null)
+ {
+ return;
+ }
+ }
+
+ Object[] args = expr.getArguments();
+ Object[] newArgs = new Object[args.length];
+
+ for (int i = 0; i < args.length; i++)
+ {
+ newArgs[i] = get(args[i]);
+ if (newArgs[i] == null || isImmutableType(args[i].getClass()))
+ {
+ writeObject(args[i]);
+ newArgs[i] = get(args[i]);
+ }
+ }
+
+ Expression newExpr = new Expression(newTarget, expr.getMethodName(),
+ newArgs);
+
+ // Fakes the result of Class.forName(<primitiveType>) to make it possible
+ // to hand such a type to the encoding process.
+ if (value instanceof Class && ((Class) value).isPrimitive())
+ newExpr.setValue(value);
+
+ // Instantiates the new object.
+ try
+ {
+ newValue = newExpr.getValue();
+
+ putCandidate(value, newValue);
+ }
+ catch (Exception e)
+ {
+ getExceptionListener().exceptionThrown(e);
+
+ // In Statement.writeExpression we had no possibility to flags
+ // an erroneous state to the ScanEngine without behaving different
+ // to the JDK.
+ scanEngine.revoke();
+ }
+
+ writeObject(value);
+
+ }
+ else if(value.getClass() == String.class || value.getClass() == Class.class)
+ {
+ writeObject(value);
+ }
+
+ scanEngine.end();
+ }
+
+ public void writeStatement(Statement stmt)
+ {
+ // In case of questions have a at the implementation note in
+ // writeExpression.
+
+ scanEngine.writeStatement(stmt);
+
+ // Silently ignore out of bounds calls.
+ if (accessCounter <= 0)
+ return;
+
+ Object target = stmt.getTarget();
+
+ Object newTarget = get(target);
+ if (newTarget == null)
+ {
+ writeObject(target);
+ newTarget = get(target);
+ }
+
+ Object[] args = stmt.getArguments();
+ Object[] newArgs = new Object[args.length];
+
+ for (int i = 0; i < args.length; i++)
+ {
+ // Here is the difference to the original writeStatement
+ // method in Encoder. In case that the object is known or
+ // not an immutable we put it directly into the ScanEngine
+ // which will then generate an object reference for it.
+ newArgs[i] = get(args[i]);
+ if (newArgs[i] == null || isImmutableType(args[i].getClass()))
+ {
+ writeObject(args[i]);
+ newArgs[i] = get(args[i]);
+ }
+ else
+ scanEngine.writeObject(args[i]);
+ }
+
+ Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs);
+
+ try
+ {
+ newStmt.execute();
+ }
+ catch (Exception e)
+ {
+ getExceptionListener().exceptionThrown(e);
+
+ // In Statement.writeStatement we had no possibility to flags
+ // an erroneous state to the ScanEngine without behaving different
+ // to the JDK.
+ scanEngine.revoke();
+ return;
+ }
+
+ scanEngine.end();
+ }
+
+ public void writeObject(Object o)
+ {
+ accessCounter++;
+
+ scanEngine.writeObject(o);
+
+ if (get(o) == null);
+ super.writeObject(o);
+
+ accessCounter--;
+ }
+
+ public void setOwner(Object o)
+ {
+ owner = o;
+ }
+
+ public Object getOwner()
+ {
+ return owner;
+ }
+
+}
diff --git a/libjava/classpath/java/io/File.java b/libjava/classpath/java/io/File.java
index 3c7ac21301c..43e8e5ded6c 100644
--- a/libjava/classpath/java/io/File.java
+++ b/libjava/classpath/java/io/File.java
@@ -396,7 +396,8 @@ public class File implements Serializable, Comparable
* 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.
+ * @param uri The URI
+ * @throws IllegalArgumentException if the URI is not hierarchical
*/
public File(URI uri)
{
@@ -406,7 +407,11 @@ public class File implements Serializable, Comparable
if (!uri.getScheme().equals("file"))
throw new IllegalArgumentException("invalid uri protocol");
- path = normalizePath(uri.getPath());
+ String name = uri.getPath();
+ if (name == null)
+ throw new IllegalArgumentException("URI \"" + uri
+ + "\" is not hierarchical");
+ path = normalizePath(name);
}
/**
diff --git a/libjava/classpath/java/io/InputStreamReader.java b/libjava/classpath/java/io/InputStreamReader.java
index 57cdc53ed22..ef8fd4542db 100644
--- a/libjava/classpath/java/io/InputStreamReader.java
+++ b/libjava/classpath/java/io/InputStreamReader.java
@@ -230,6 +230,8 @@ public class InputStreamReader extends Reader
* Creates an InputStreamReader that uses a decoder of the given
* charset to decode the bytes in the InputStream into
* characters.
+ *
+ * @since 1.5
*/
public InputStreamReader(InputStream in, Charset charset) {
this.in = in;
@@ -244,6 +246,8 @@ public class InputStreamReader extends Reader
/**
* Creates an InputStreamReader that uses the given charset decoder
* to decode the bytes in the InputStream into characters.
+ *
+ * @since 1.5
*/
public InputStreamReader(InputStream in, CharsetDecoder decoder) {
this.in = in;
diff --git a/libjava/classpath/java/io/ObjectInputStream.java b/libjava/classpath/java/io/ObjectInputStream.java
index 98a11dae3e1..750c6989f25 100644
--- a/libjava/classpath/java/io/ObjectInputStream.java
+++ b/libjava/classpath/java/io/ObjectInputStream.java
@@ -50,7 +50,6 @@ 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.Iterator;
import java.util.TreeSet;
diff --git a/libjava/classpath/java/io/ObjectOutputStream.java b/libjava/classpath/java/io/ObjectOutputStream.java
index 573b9cfa1de..961d5e3099f 100644
--- a/libjava/classpath/java/io/ObjectOutputStream.java
+++ b/libjava/classpath/java/io/ObjectOutputStream.java
@@ -442,6 +442,11 @@ public class ObjectOutputStream extends OutputStream
realOutput.writeByte(flags);
ObjectStreamField[] fields = osc.fields;
+
+ if (fields == ObjectStreamClass.INVALID_FIELDS)
+ throw new InvalidClassException
+ (osc.getName(), "serialPersistentFields is invalid");
+
realOutput.writeShort(fields.length);
ObjectStreamField field;
diff --git a/libjava/classpath/java/io/ObjectStreamClass.java b/libjava/classpath/java/io/ObjectStreamClass.java
index 975dbfc66d0..203e4a5abaa 100644
--- a/libjava/classpath/java/io/ObjectStreamClass.java
+++ b/libjava/classpath/java/io/ObjectStreamClass.java
@@ -63,6 +63,8 @@ import java.util.Vector;
public class ObjectStreamClass implements Serializable
{
+ static final ObjectStreamField[] INVALID_FIELDS = new ObjectStreamField[0];
+
/**
* Returns the <code>ObjectStreamClass</code> for <code>cl</code>.
* If <code>cl</code> is null, or is not <code>Serializable</code>,
@@ -71,6 +73,11 @@ public class ObjectStreamClass implements Serializable
* same <code>ObjectStreamClass</code> object and no recalculation
* will be done.
*
+ * Warning: If this class contains an invalid serialPersistentField arrays
+ * lookup will not throw anything. However {@link #getFields()} will return
+ * an empty array and {@link java.io.ObjectOutputStream#writeObject} will throw an
+ * {@link java.io.InvalidClassException}.
+ *
* @see java.io.Serializable
*/
public static ObjectStreamClass lookup(Class cl)
@@ -148,6 +155,8 @@ public class ObjectStreamClass implements Serializable
* Returns the serializable (non-static and non-transient) Fields
* of the class represented by this ObjectStreamClass. The Fields
* are sorted by name.
+ * If fields were obtained using serialPersistentFields and this array
+ * is faulty then the returned array of this method will be empty.
*
* @return the fields.
*/
@@ -608,6 +617,28 @@ outer:
fields = getSerialPersistentFields(cl);
if (fields != null)
{
+ ObjectStreamField[] fieldsName = new ObjectStreamField[fields.length];
+ System.arraycopy(fields, 0, fieldsName, 0, fields.length);
+
+ Arrays.sort (fieldsName, new Comparator() {
+ public int compare(Object o1, Object o2)
+ {
+ ObjectStreamField f1 = (ObjectStreamField)o1;
+ ObjectStreamField f2 = (ObjectStreamField)o2;
+
+ return f1.getName().compareTo(f2.getName());
+ }
+ });
+
+ for (int i=1; i < fields.length; i++)
+ {
+ if (fieldsName[i-1].getName().equals(fieldsName[i].getName()))
+ {
+ fields = INVALID_FIELDS;
+ return;
+ }
+ }
+
Arrays.sort (fields);
// Retrieve field reference.
for (int i=0; i < fields.length; i++)
diff --git a/libjava/classpath/java/io/OutputStreamWriter.java b/libjava/classpath/java/io/OutputStreamWriter.java
index 29fb631faf4..572683834be 100644
--- a/libjava/classpath/java/io/OutputStreamWriter.java
+++ b/libjava/classpath/java/io/OutputStreamWriter.java
@@ -213,6 +213,8 @@ public class OutputStreamWriter extends Writer
*
* @param out The <code>OutputStream</code> to write to
* @param cs The <code>Charset</code> of the encoding to use
+ *
+ * @since 1.5
*/
public OutputStreamWriter(OutputStream out, Charset cs)
{
@@ -230,6 +232,8 @@ public class OutputStreamWriter extends Writer
*
* @param out The <code>OutputStream</code> to write to
* @param enc The <code>CharsetEncoder</code> to encode the output with
+ *
+ * @since 1.5
*/
public OutputStreamWriter(OutputStream out, CharsetEncoder enc)
{
diff --git a/libjava/classpath/java/io/PrintStream.java b/libjava/classpath/java/io/PrintStream.java
index 8e50559b310..99af25583b0 100644
--- a/libjava/classpath/java/io/PrintStream.java
+++ b/libjava/classpath/java/io/PrintStream.java
@@ -1,5 +1,6 @@
/* PrintStream.java -- OutputStream for printing output
- Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -38,6 +39,8 @@ exception statement from your version. */
package java.io;
+import gnu.classpath.SystemProperties;
+
/* 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
@@ -64,7 +67,7 @@ public class PrintStream extends FilterOutputStream
// Line separator string.
private static final char[] line_separator
- = System.getProperty("line.separator").toCharArray();
+ = SystemProperties.getProperty("line.separator").toCharArray();
/**
* Encoding name
@@ -112,7 +115,7 @@ public class PrintStream extends FilterOutputStream
super (out);
try {
- this.encoding = System.getProperty("file.encoding");
+ this.encoding = SystemProperties.getProperty("file.encoding");
} catch (SecurityException e){
this.encoding = "ISO8859_1";
} catch (IllegalArgumentException e){
diff --git a/libjava/classpath/java/io/RandomAccessFile.java b/libjava/classpath/java/io/RandomAccessFile.java
index 23be5a31947..84ee5dec01e 100644
--- a/libjava/classpath/java/io/RandomAccessFile.java
+++ b/libjava/classpath/java/io/RandomAccessFile.java
@@ -124,7 +124,10 @@ public class RandomAccessFile implements DataOutput, DataInput
ch = FileChannelImpl.create(file, fdmode);
fd = new FileDescriptor(ch);
- out = new DataOutputStream (new FileOutputStream (fd));
+ if ((fdmode & FileChannelImpl.WRITE) != 0)
+ out = new DataOutputStream (new FileOutputStream (fd));
+ else
+ out = null;
in = new DataInputStream (new FileInputStream (fd));
}
@@ -766,6 +769,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public void write (int oneByte) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.write(oneByte);
}
@@ -777,6 +783,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public void write (byte[] buffer) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.write(buffer);
}
@@ -792,6 +801,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public void write (byte[] buffer, int offset, int len) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.write (buffer, offset, len);
}
@@ -806,6 +818,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeBoolean (boolean val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeBoolean(val);
}
@@ -820,6 +835,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeByte (int val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeByte(val);
}
@@ -834,6 +852,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeShort (int val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeShort(val);
}
@@ -848,6 +869,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeChar (int val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeChar(val);
}
@@ -861,6 +885,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeInt (int val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeInt(val);
}
@@ -874,6 +901,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeLong (long val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeLong(val);
}
@@ -893,6 +923,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeFloat (float val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeFloat(val);
}
@@ -913,6 +946,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeDouble (double val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeDouble(val);
}
@@ -927,6 +963,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeBytes (String val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeBytes(val);
}
@@ -941,6 +980,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeChars (String val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeChars(val);
}
@@ -975,6 +1017,9 @@ public class RandomAccessFile implements DataOutput, DataInput
*/
public final void writeUTF (String val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeUTF(val);
}
diff --git a/libjava/classpath/java/io/StreamTokenizer.java b/libjava/classpath/java/io/StreamTokenizer.java
index bd7773b1990..b4695ab3d09 100644
--- a/libjava/classpath/java/io/StreamTokenizer.java
+++ b/libjava/classpath/java/io/StreamTokenizer.java
@@ -550,6 +550,12 @@ public class StreamTokenizer
/**
* This method sets the numeric attribute on the characters '0' - '9' and
* the characters '.' and '-'.
+ * When this method is used, the result of giving other attributes
+ * (whitespace, quote, or comment) to the numeric characters may
+ * vary depending on the implementation. For example, if
+ * parseNumbers() and then whitespaceChars('1', '1') are called,
+ * this implementation reads "121" as 2, while some other implementation
+ * will read it as 21.
*/
public void parseNumbers()
{
diff --git a/libjava/classpath/java/lang/Character.java b/libjava/classpath/java/lang/Character.java
index 78db41ef216..3c88ff805c7 100644
--- a/libjava/classpath/java/lang/Character.java
+++ b/libjava/classpath/java/lang/Character.java
@@ -2410,11 +2410,11 @@ public final class Character implements Serializable, Comparable
{
// Write second char first to cause IndexOutOfBoundsException
// immediately.
- dst[dstIndex + 1] = (char) ((codePoint & 0x3ff)
- + (int) MIN_LOW_SURROGATE );
- dst[dstIndex] = (char) ((codePoint >> 10) + (int) MIN_HIGH_SURROGATE);
+ final int cp2 = codePoint - 0x10000;
+ dst[dstIndex + 1] = (char) ((cp2 % 0x400) + (int) MIN_LOW_SURROGATE);
+ dst[dstIndex] = (char) ((cp2 / 0x400) + (int) MIN_HIGH_SURROGATE);
result = 2;
- }
+ }
else
{
dst[dstIndex] = (char) codePoint;
@@ -2523,7 +2523,8 @@ public final class Character implements Serializable, Comparable
*/
public static int toCodePoint(char high, char low)
{
- return ((high - MIN_HIGH_SURROGATE) << 10) + (low - MIN_LOW_SURROGATE);
+ return ((high - MIN_HIGH_SURROGATE) * 0x400) +
+ (low - MIN_LOW_SURROGATE) + 0x10000;
}
/**
diff --git a/libjava/classpath/java/lang/Class.java b/libjava/classpath/java/lang/Class.java
index 726c794a413..c4235e6808c 100644
--- a/libjava/classpath/java/lang/Class.java
+++ b/libjava/classpath/java/lang/Class.java
@@ -583,8 +583,7 @@ public final class Class implements Serializable
/**
* 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.
+ * classloader of this class.
*
* @return the package for this class, if it is available
* @since 1.2
@@ -837,7 +836,10 @@ public final class Class implements Serializable
*/
public int getModifiers()
{
- return VMClass.getModifiers (this, false);
+ int mod = VMClass.getModifiers (this, false);
+ return (mod & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE |
+ Modifier.FINAL | Modifier.STATIC | Modifier.ABSTRACT |
+ Modifier.INTERFACE));
}
/**
diff --git a/libjava/classpath/java/lang/Double.java b/libjava/classpath/java/lang/Double.java
index 26b398bb695..03c56068921 100644
--- a/libjava/classpath/java/lang/Double.java
+++ b/libjava/classpath/java/lang/Double.java
@@ -173,6 +173,81 @@ public final class Double extends Number implements Comparable
}
/**
+ * Convert a double value to a hexadecimal string. This converts as
+ * follows:
+ * <ul>
+ * <li> A NaN value is converted to the string "NaN".
+ * <li> Positive infinity is converted to the string "Infinity".
+ * <li> Negative infinity is converted to the string "-Infinity".
+ * <li> For all other values, the first character of the result is '-'
+ * if the value is negative. This is followed by '0x1.' if the
+ * value is normal, and '0x0.' if the value is denormal. This is
+ * then followed by a (lower-case) hexadecimal representation of the
+ * mantissa, with leading zeros as required for denormal values.
+ * The next character is a 'p', and this is followed by a decimal
+ * representation of the unbiased exponent.
+ * </ul>
+ * @param d the double value
+ * @return the hexadecimal string representation
+ * @since 1.5
+ */
+ public static String toHexString(double d)
+ {
+ if (isNaN(d))
+ return "NaN";
+ if (isInfinite(d))
+ return d < 0 ? "-Infinity" : "Infinity";
+
+ long bits = doubleToLongBits(d);
+ StringBuilder result = new StringBuilder();
+
+ if (bits < 0)
+ result.append('-');
+ result.append("0x");
+
+ final int mantissaBits = 52;
+ final int exponentBits = 11;
+ long mantMask = (1L << mantissaBits) - 1;
+ long mantissa = bits & mantMask;
+ long expMask = (1L << exponentBits) - 1;
+ long exponent = (bits >>> mantissaBits) & expMask;
+
+ result.append(exponent == 0 ? '0' : '1');
+ result.append('.');
+ result.append(Long.toHexString(mantissa));
+ if (exponent == 0 && mantissa != 0)
+ {
+ // Treat denormal specially by inserting '0's to make
+ // the length come out right. The constants here are
+ // to account for things like the '0x'.
+ int offset = 4 + ((bits < 0) ? 1 : 0);
+ // The silly +3 is here to keep the code the same between
+ // the Float and Double cases. In Float the value is
+ // not a multiple of 4.
+ int desiredLength = offset + (mantissaBits + 3) / 4;
+ while (result.length() < desiredLength)
+ result.insert(offset, '0');
+ }
+ result.append('p');
+ if (exponent == 0 && mantissa == 0)
+ {
+ // Zero, so do nothing special.
+ }
+ else
+ {
+ // Apply bias.
+ boolean denormal = exponent == 0;
+ exponent -= (1 << (exponentBits - 1)) - 1;
+ // Handle denormal.
+ if (denormal)
+ ++exponent;
+ }
+
+ result.append(Long.toString(exponent));
+ return result.toString();
+ }
+
+ /**
* Returns a <code>Double</code> object wrapping the value.
* In contrast to the <code>Double</code> constructor, this method
* may cache some values. It is used by boxing conversion.
diff --git a/libjava/classpath/java/lang/Float.java b/libjava/classpath/java/lang/Float.java
index eef34a0abeb..dcd5b221197 100644
--- a/libjava/classpath/java/lang/Float.java
+++ b/libjava/classpath/java/lang/Float.java
@@ -183,6 +183,83 @@ public final class Float extends Number implements Comparable
}
/**
+ * Convert a float value to a hexadecimal string. This converts as
+ * follows:
+ * <ul>
+ * <li> A NaN value is converted to the string "NaN".
+ * <li> Positive infinity is converted to the string "Infinity".
+ * <li> Negative infinity is converted to the string "-Infinity".
+ * <li> For all other values, the first character of the result is '-'
+ * if the value is negative. This is followed by '0x1.' if the
+ * value is normal, and '0x0.' if the value is denormal. This is
+ * then followed by a (lower-case) hexadecimal representation of the
+ * mantissa, with leading zeros as required for denormal values.
+ * The next character is a 'p', and this is followed by a decimal
+ * representation of the unbiased exponent.
+ * </ul>
+ * @param f the float value
+ * @return the hexadecimal string representation
+ * @since 1.5
+ */
+ public static String toHexString(float f)
+ {
+ if (isNaN(f))
+ return "NaN";
+ if (isInfinite(f))
+ return f < 0 ? "-Infinity" : "Infinity";
+
+ int bits = floatToIntBits(f);
+ StringBuilder result = new StringBuilder();
+
+ if (bits < 0)
+ result.append('-');
+ result.append("0x");
+
+ final int mantissaBits = 23;
+ final int exponentBits = 8;
+ int mantMask = (1 << mantissaBits) - 1;
+ int mantissa = bits & mantMask;
+ int expMask = (1 << exponentBits) - 1;
+ int exponent = (bits >>> mantissaBits) & expMask;
+
+ result.append(exponent == 0 ? '0' : '1');
+ result.append('.');
+ // For Float only, we have to adjust the mantissa.
+ mantissa <<= 1;
+ result.append(Integer.toHexString(mantissa));
+ if (exponent == 0 && mantissa != 0)
+ {
+ // Treat denormal specially by inserting '0's to make
+ // the length come out right. The constants here are
+ // to account for things like the '0x'.
+ int offset = 4 + ((bits < 0) ? 1 : 0);
+ // The silly +3 is here to keep the code the same between
+ // the Float and Double cases. In Float the value is
+ // not a multiple of 4.
+ int desiredLength = offset + (mantissaBits + 3) / 4;
+ while (result.length() < desiredLength)
+ result.insert(offset, '0');
+ }
+ result.append('p');
+ if (exponent == 0 && mantissa == 0)
+ {
+ // Zero, so do nothing special.
+ }
+ else
+ {
+ // Apply bias.
+ boolean denormal = exponent == 0;
+ exponent -= (1 << (exponentBits - 1)) - 1;
+ // Handle denormal.
+ if (denormal)
+ ++exponent;
+ }
+
+ result.append(Integer.toString(exponent));
+ return result.toString();
+ }
+
+ /**
* Creates a new <code>Float</code> object using the <code>String</code>.
*
* @param s the <code>String</code> to convert
diff --git a/libjava/classpath/java/lang/InheritableThreadLocal.java b/libjava/classpath/java/lang/InheritableThreadLocal.java
index 69ff6138dc6..b9c7624ef44 100644
--- a/libjava/classpath/java/lang/InheritableThreadLocal.java
+++ b/libjava/classpath/java/lang/InheritableThreadLocal.java
@@ -1,5 +1,5 @@
/* InheritableThreadLocal -- a ThreadLocal which inherits values across threads
- Copyright (C) 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2000, 2001, 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -37,8 +37,9 @@ exception statement from your version. */
package java.lang;
+import gnu.java.util.WeakIdentityHashMap;
+
import java.util.Iterator;
-import java.util.WeakHashMap;
/**
* A ThreadLocal whose value is inherited by child Threads. The value of the
@@ -98,15 +99,15 @@ public class InheritableThreadLocal extends ThreadLocal
Iterator keys = parentThread.locals.keySet().iterator();
while (keys.hasNext())
{
- Key key = (Key)keys.next();
- if (key.get() instanceof InheritableThreadLocal)
+ Object key = keys.next();
+ if (key instanceof InheritableThreadLocal)
{
- InheritableThreadLocal local = (InheritableThreadLocal)key.get();
+ InheritableThreadLocal local = (InheritableThreadLocal)key;
Object parentValue = parentThread.locals.get(key);
Object childValue = local.childValue(parentValue == NULL
? null : parentValue);
if (childThread.locals == null)
- childThread.locals = new WeakHashMap();
+ childThread.locals = new WeakIdentityHashMap();
childThread.locals.put(key, (childValue == null
? NULL : childValue));
}
diff --git a/libjava/classpath/java/lang/SecurityManager.java b/libjava/classpath/java/lang/SecurityManager.java
index 26d56a64bf3..30ee1be086f 100644
--- a/libjava/classpath/java/lang/SecurityManager.java
+++ b/libjava/classpath/java/lang/SecurityManager.java
@@ -41,9 +41,6 @@ package java.lang;
import gnu.classpath.VMStackWalker;
import java.awt.AWTPermission;
-import java.awt.Frame;
-import java.awt.Toolkit;
-import java.awt.Window;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -424,7 +421,7 @@ public class SecurityManager
public void checkAccess(Thread thread)
{
if (thread.getThreadGroup() != null
- && thread.getThreadGroup().getParent() != null)
+ && thread.getThreadGroup().getParent() == null)
checkPermission(new RuntimePermission("modifyThread"));
}
@@ -457,7 +454,7 @@ public class SecurityManager
*/
public void checkAccess(ThreadGroup g)
{
- if (g.getParent() != null)
+ if (g.getParent() == null)
checkPermission(new RuntimePermission("modifyThreadGroup"));
}
@@ -837,7 +834,7 @@ public class SecurityManager
* @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)
+ * @see java.awt.Window#Window(java.awt.Frame)
*/
public boolean checkTopLevelWindow(Object window)
{
@@ -862,7 +859,7 @@ public class SecurityManager
* an exception.
*
* @throws SecurityException if permission is denied
- * @see Toolkit#getPrintJob(Frame, String, Properties)
+ * @see java.awt.Toolkit#getPrintJob(java.awt.Frame, String, Properties)
* @since 1.1
*/
public void checkPrintJobAccess()
@@ -878,7 +875,7 @@ public class SecurityManager
* rather than throwing an exception.
*
* @throws SecurityException if permission is denied
- * @see Toolkit#getSystemClipboard()
+ * @see java.awt.Toolkit#getSystemClipboard()
* @since 1.1
*/
public void checkSystemClipboardAccess()
@@ -894,7 +891,7 @@ public class SecurityManager
* rather than throwing an exception.
*
* @throws SecurityException if permission is denied
- * @see Toolkit#getSystemEventQueue()
+ * @see java.awt.Toolkit#getSystemEventQueue()
* @since 1.1
*/
public void checkAwtEventQueueAccess()
diff --git a/libjava/classpath/java/lang/StackTraceElement.java b/libjava/classpath/java/lang/StackTraceElement.java
index 6dd4d8532e8..cf4d1c76f4d 100644
--- a/libjava/classpath/java/lang/StackTraceElement.java
+++ b/libjava/classpath/java/lang/StackTraceElement.java
@@ -49,7 +49,7 @@ import java.io.Serializable;
* @author Mark Wielaard (mark@klomp.org)
* @author Eric Blake (ebb9@email.byu.edu)
* @since 1.4
- * @status updated to 1.4
+ * @status updated to 1.5
*/
public final class StackTraceElement implements Serializable
{
@@ -112,6 +112,26 @@ public final class StackTraceElement implements Serializable
}
/**
+ * Create a new StackTraceElement representing a given source location.
+ *
+ * @param className the fully qualified name of the class
+ * @param methodName the name of the method
+ * @param fileName the name of the file, null if unknown
+ * @param lineNumber the line in the file, negative if unknown, or -2
+ * if this method is native
+ *
+ * @since 1.5
+ */
+ public StackTraceElement(String className, String methodName, String fileName,
+ int lineNumber)
+ {
+ this(fileName, lineNumber, className, methodName, lineNumber == -2);
+ // The public constructor doesn't allow certain values to be null.
+ if (className == null || methodName == null)
+ throw new NullPointerException("invalid argument to constructor");
+ }
+
+ /**
* 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.
diff --git a/libjava/classpath/java/lang/String.java b/libjava/classpath/java/lang/String.java
index 369d8085a02..231afc77b92 100644
--- a/libjava/classpath/java/lang/String.java
+++ b/libjava/classpath/java/lang/String.java
@@ -273,7 +273,8 @@ public final class String implements Serializable, Comparable, CharSequence
throw new StringIndexOutOfBoundsException("offset: " + offset);
if (count < 0)
throw new StringIndexOutOfBoundsException("count: " + count);
- if (offset + count < 0 || offset + count > ascii.length)
+ // equivalent to: offset + count < 0 || offset + count > ascii.length
+ if (ascii.length - offset < count)
throw new StringIndexOutOfBoundsException("offset + count: "
+ (offset + count));
value = new char[count];
@@ -338,7 +339,8 @@ public final class String implements Serializable, Comparable, CharSequence
throw new StringIndexOutOfBoundsException("offset: " + offset);
if (count < 0)
throw new StringIndexOutOfBoundsException("count: " + count);
- if (offset + count < 0 || offset + count > data.length)
+ // equivalent to: offset + count < 0 || offset + count > data.length
+ if (data.length - offset < count)
throw new StringIndexOutOfBoundsException("offset + count: "
+ (offset + count));
try
@@ -418,7 +420,8 @@ public final class String implements Serializable, Comparable, CharSequence
throw new StringIndexOutOfBoundsException("offset: " + offset);
if (count < 0)
throw new StringIndexOutOfBoundsException("count: " + count);
- if (offset + count < 0 || offset + count > data.length)
+ // equivalent to: offset + count < 0 || offset + count > data.length
+ if (data.length - offset < count)
throw new StringIndexOutOfBoundsException("offset + count: "
+ (offset + count));
int o, c;
@@ -533,7 +536,8 @@ public final class String implements Serializable, Comparable, CharSequence
throw new StringIndexOutOfBoundsException("offset: " + offset);
if (count < 0)
throw new StringIndexOutOfBoundsException("count: " + count);
- if (offset + count < 0 || offset + count > data.length)
+ // equivalent to: offset + count < 0 || offset + count > data.length
+ if (data.length - offset < count)
throw new StringIndexOutOfBoundsException("offset + count: "
+ (offset + count));
if (dont_copy)
@@ -1761,7 +1765,7 @@ public final class String implements Serializable, Comparable, CharSequence
/**
* Return the number of code points between two indices in the
- * <code>StringBuffer</code>. An unpaired surrogate counts as a
+ * <code>String</code>. An unpaired surrogate counts as a
* code point for this purpose. Characters outside the indicated
* range are not examined, even if the range ends in the middle of a
* surrogate pair.
@@ -1879,6 +1883,8 @@ public final class String implements Serializable, Comparable, CharSequence
* described in s.
* @param s the CharSequence
* @return true iff this String contains s
+ *
+ * @since 1.5
*/
public boolean contains (CharSequence s)
{
diff --git a/libjava/classpath/java/lang/Thread.java b/libjava/classpath/java/lang/Thread.java
index 763228c16ef..9afde5bfd03 100644
--- a/libjava/classpath/java/lang/Thread.java
+++ b/libjava/classpath/java/lang/Thread.java
@@ -38,9 +38,9 @@ exception statement from your version. */
package java.lang;
+import gnu.java.util.WeakIdentityHashMap;
import java.security.Permission;
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
@@ -137,7 +137,7 @@ public class Thread implements Runnable
/** Thread local storage. Package accessible for use by
* InheritableThreadLocal.
*/
- WeakHashMap locals;
+ WeakIdentityHashMap locals;
/**
* Allocates a new <code>Thread</code> object. This constructor has
@@ -996,7 +996,7 @@ public class Thread implements Runnable
Map locals = thread.locals;
if (locals == null)
{
- locals = thread.locals = new WeakHashMap();
+ locals = thread.locals = new WeakIdentityHashMap();
}
return locals;
}
diff --git a/libjava/classpath/java/lang/ThreadLocal.java b/libjava/classpath/java/lang/ThreadLocal.java
index bc839044574..aceb2557a54 100644
--- a/libjava/classpath/java/lang/ThreadLocal.java
+++ b/libjava/classpath/java/lang/ThreadLocal.java
@@ -1,5 +1,5 @@
/* ThreadLocal -- a variable with a unique value per thread
- Copyright (C) 2000, 2002, 2003 Free Software Foundation, Inc.
+ Copyright (C) 2000, 2002, 2003, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -96,21 +96,6 @@ public class ThreadLocal
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()
@@ -143,11 +128,11 @@ public class ThreadLocal
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);
+ Object value = map.get(this);
if (value == null)
{
value = initialValue();
- map.put(key, value == null ? NULL : value);
+ map.put(this, value == null ? NULL : value);
}
return value == NULL ? null : value;
}
@@ -165,6 +150,6 @@ public class ThreadLocal
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);
+ map.put(this, value == null ? NULL : value);
}
}
diff --git a/libjava/classpath/java/net/DatagramSocket.java b/libjava/classpath/java/net/DatagramSocket.java
index 40bafbb34dd..d8837c006b5 100644
--- a/libjava/classpath/java/net/DatagramSocket.java
+++ b/libjava/classpath/java/net/DatagramSocket.java
@@ -176,7 +176,12 @@ public class DatagramSocket
{
String propVal = SystemProperties.getProperty("impl.prefix");
if (propVal == null || propVal.equals(""))
- impl = new PlainDatagramSocketImpl();
+ {
+ if (factory != null)
+ impl = factory.createDatagramSocketImpl();
+ else
+ impl = new PlainDatagramSocketImpl();
+ }
else
try
{
diff --git a/libjava/classpath/java/net/InetAddress.java b/libjava/classpath/java/net/InetAddress.java
index 94dc6cb6d84..7277331bb26 100644
--- a/libjava/classpath/java/net/InetAddress.java
+++ b/libjava/classpath/java/net/InetAddress.java
@@ -43,7 +43,6 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
-import java.util.HashMap;
import java.util.StringTokenizer;
/**
@@ -66,22 +65,6 @@ 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;
@@ -96,50 +79,8 @@ public class InetAddress implements Serializable
*/
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
{
@@ -174,11 +115,6 @@ public class InetAddress implements Serializable
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
@@ -200,8 +136,6 @@ public class InetAddress implements Serializable
addr = (null == ipaddr) ? null : (byte[]) ipaddr.clone();
hostName = hostname;
- lookup_time = System.currentTimeMillis();
-
family = 2; /* AF_INET */
}
@@ -649,20 +583,17 @@ public class InetAddress implements Serializable
InetAddress[] addresses;
+ if (hostname != null)
+ hostname = hostname.trim();
+
// Default to current host if necessary
- if (hostname == null)
+ if (hostname == null || hostname.equals(""))
{
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);
@@ -679,71 +610,10 @@ public class InetAddress implements Serializable
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.
*
@@ -757,6 +627,7 @@ public class InetAddress implements Serializable
{
byte[] tmp = VMInetAddress.lookupInaddrAny();
inaddr_any = new Inet4Address(tmp, null);
+ inaddr_any.hostName = inaddr_any.getHostName();
}
return inaddr_any;
diff --git a/libjava/classpath/java/net/URL.java b/libjava/classpath/java/net/URL.java
index 1d947a0b46a..168c67a19ee 100644
--- a/libjava/classpath/java/net/URL.java
+++ b/libjava/classpath/java/net/URL.java
@@ -1,5 +1,5 @@
/* URL.java -- Uniform Resource Locator Class
- Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005
+ Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -38,6 +38,7 @@ exception statement from your version. */
package java.net;
+import gnu.classpath.SystemProperties;
import gnu.java.net.URLParseError;
import java.io.IOException;
@@ -198,7 +199,7 @@ public final class URL implements Serializable
static
{
- String s = System.getProperty("gnu.java.net.nocache_protocol_handlers");
+ String s = SystemProperties.getProperty("gnu.java.net.nocache_protocol_handlers");
if (s == null)
cache_handlers = true;
@@ -342,7 +343,7 @@ public final class URL implements Serializable
*/
public URL(URL context, String spec) throws MalformedURLException
{
- this(context, spec, (URLStreamHandler) null);
+ this(context, spec, (context == null) ? (URLStreamHandler)null : context.ph);
}
/**
@@ -867,7 +868,7 @@ public final class URL implements Serializable
// 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");
+ SystemProperties.getProperty("java.protocol.handler.pkgs");
// Tack our default package on at the ends.
if (ph_search_path != null)
diff --git a/libjava/classpath/java/nio/charset/Charset.java b/libjava/classpath/java/nio/charset/Charset.java
index 91801ddac87..3637703a32c 100644
--- a/libjava/classpath/java/nio/charset/Charset.java
+++ b/libjava/classpath/java/nio/charset/Charset.java
@@ -121,6 +121,8 @@ public abstract class Charset implements Comparable
*
* This may be set by the user or VM with the file.encoding
* property.
+ *
+ * @since 1.5
*/
public static Charset defaultCharset()
{
diff --git a/libjava/classpath/java/nio/charset/spi/CharsetProvider.java b/libjava/classpath/java/nio/charset/spi/CharsetProvider.java
index f0d40ab8591..496ccf92789 100644
--- a/libjava/classpath/java/nio/charset/spi/CharsetProvider.java
+++ b/libjava/classpath/java/nio/charset/spi/CharsetProvider.java
@@ -1,5 +1,5 @@
/* CharsetProvider.java -- charset service provider interface
- Copyright (C) 2002 Free Software Foundation
+ Copyright (C) 2002, 2006 Free Software Foundation
This file is part of GNU Classpath.
@@ -67,8 +67,12 @@ public abstract class CharsetProvider
*/
protected CharsetProvider()
{
+ // We only do the security check for custom providers, not for the
+ // built in ones.
SecurityManager s = System.getSecurityManager();
- if (s != null)
+ if (s != null &&
+ ! (this instanceof gnu.java.nio.charset.Provider
+ || this instanceof gnu.java.nio.charset.iconv.IconvProvider))
s.checkPermission(new RuntimePermission("charsetProvider"));
}
diff --git a/libjava/classpath/java/security/MessageDigest.java b/libjava/classpath/java/security/MessageDigest.java
index 8684f2083b1..8a6af645be1 100644
--- a/libjava/classpath/java/security/MessageDigest.java
+++ b/libjava/classpath/java/security/MessageDigest.java
@@ -1,5 +1,5 @@
/* MessageDigest.java --- The message digest interface.
- Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2002, 2003, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -167,6 +167,9 @@ public abstract class MessageDigest extends MessageDigestSpi
public static MessageDigest getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException
{
+ if (provider != null)
+ provider = provider.trim();
+
if (provider == null || provider.length() == 0)
throw new IllegalArgumentException("Illegal provider");
diff --git a/libjava/classpath/java/security/Security.java b/libjava/classpath/java/security/Security.java
index fd51d0535b3..d26d049c524 100644
--- a/libjava/classpath/java/security/Security.java
+++ b/libjava/classpath/java/security/Security.java
@@ -1,5 +1,6 @@
/* Security.java --- Java base security class implementation
- Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -41,6 +42,7 @@ package java.security;
import gnu.classpath.SystemProperties;
import gnu.classpath.Configuration;
+import gnu.classpath.VMStackWalker;
import java.io.IOException;
import java.io.InputStream;
@@ -354,6 +356,14 @@ public final class Security
*/
public static Provider getProvider(String name)
{
+ if (name == null)
+ return null;
+ else
+ {
+ name = name.trim();
+ if (name.length() == 0)
+ return null;
+ }
Provider p;
int max = providers.size ();
for (int i = 0; i < max; i++)
@@ -383,8 +393,11 @@ public final class Security
*/
public static String getProperty(String key)
{
+ // XXX To prevent infinite recursion when the SecurityManager calls us,
+ // don't do a security check if the caller is trusted (by virtue of having
+ // been loaded by the bootstrap class loader).
SecurityManager sm = System.getSecurityManager();
- if (sm != null)
+ if (sm != null && VMStackWalker.getCallingClassLoader() != null)
sm.checkSecurityAccess("getProperty." + key);
return secprops.getProperty(key);
@@ -399,20 +412,23 @@ public final class Security
* </p>
*
* @param key the name of the property to be set.
- * @param datnum the value of the property to be set.
+ * @param datum 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)
+ public static void setProperty(String key, String datum)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkSecurityAccess("setProperty." + key);
- secprops.put(key, datnum);
+ if (datum == null)
+ secprops.remove(key);
+ else
+ secprops.put(key, datum);
}
/**
diff --git a/libjava/classpath/java/text/Bidi.java b/libjava/classpath/java/text/Bidi.java
new file mode 100644
index 00000000000..57b9a88dfa7
--- /dev/null
+++ b/libjava/classpath/java/text/Bidi.java
@@ -0,0 +1,78 @@
+/* Bidi.java -- Bidirectional Algorithm implementation
+ 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. */
+
+
+package java.text;
+
+/**
+ * Bidirectional Algorithm implementation.
+ *
+ * TODO/FIXME Only one method <code>requiresBidi</code> is implemented
+ * for now by using <code>Character</code>. The full algorithm is <a
+ * href="http://www.unicode.org/unicode/reports/tr9/">Unicode Standard
+ * Annex #9: The Bidirectional Algorithm</a>. A full implementation is
+ * <a href="http://fribidi.org/">GNU FriBidi</a>.
+ */
+public class Bidi
+{
+ /**
+ * Returns false if all characters in the text between start and end
+ * are all left-to-right text. This implementation is just calls
+ * <code>Character.getDirectionality(char)</code> on all characters
+ * and makes sure all characters are either explicitly left-to-right
+ * or neutral in directionality (character types L, EN, ES, ET, AN,
+ * CS, S and WS).
+ */
+ public static boolean requiresBidi(char[] text, int start, int end)
+ {
+ for (int i = start; i < end; i++)
+ {
+ byte dir = Character.getDirectionality(text[i]);
+ if (dir != Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ && dir != Character.DIRECTIONALITY_EUROPEAN_NUMBER
+ && dir != Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR
+ && dir != Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
+ && dir != Character.DIRECTIONALITY_ARABIC_NUMBER
+ && dir != Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR
+ && dir != Character.DIRECTIONALITY_SEGMENT_SEPARATOR
+ && dir != Character.DIRECTIONALITY_WHITESPACE)
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/libjava/classpath/java/text/DecimalFormat.java b/libjava/classpath/java/text/DecimalFormat.java
index 6dadb0ce333..a9ec7767f94 100644
--- a/libjava/classpath/java/text/DecimalFormat.java
+++ b/libjava/classpath/java/text/DecimalFormat.java
@@ -182,7 +182,9 @@ public class DecimalFormat extends NumberFormat
{
groupingUsed = saw_group;
groupingSize = (byte) countSinceGroup;
- minimumIntegerDigits = zeroCount;
+ // Checking "zeroCount > 0" avoids 0 being formatted into "" with "#".
+ if (zeroCount > 0)
+ minimumIntegerDigits = zeroCount;
}
// Early termination.
diff --git a/libjava/classpath/java/util/AbstractMap.java b/libjava/classpath/java/util/AbstractMap.java
index 7cd6436a308..b4ab882addf 100644
--- a/libjava/classpath/java/util/AbstractMap.java
+++ b/libjava/classpath/java/util/AbstractMap.java
@@ -594,13 +594,13 @@ public abstract class AbstractMap implements Map
*
* @param o1 the first object
* @param o2 the second object
- * @return o1 == null ? o2 == null : o1.equals(o2)
+ * @return o1 == o2 || (o1 != 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);
+ return o1 == o2 || (o1 != null && o1.equals(o2));
}
/**
diff --git a/libjava/classpath/java/util/ArrayList.java b/libjava/classpath/java/util/ArrayList.java
index 752f9da4ee0..50b5638ede0 100644
--- a/libjava/classpath/java/util/ArrayList.java
+++ b/libjava/classpath/java/util/ArrayList.java
@@ -92,7 +92,7 @@ public class ArrayList extends AbstractList
/**
* The default capacity for new ArrayLists.
*/
- private static final int DEFAULT_CAPACITY = 16;
+ private static final int DEFAULT_CAPACITY = 10;
/**
* The number of elements in this list.
diff --git a/libjava/classpath/java/util/Collections.java b/libjava/classpath/java/util/Collections.java
index e650bf8bda9..dc37bad8a93 100644
--- a/libjava/classpath/java/util/Collections.java
+++ b/libjava/classpath/java/util/Collections.java
@@ -670,10 +670,10 @@ public class Collections
for ( ; i != pos; i--, o = itr.previous());
forward = false;
}
- final int d = compare(key, o, c);
+ final int d = compare(o, key, c);
if (d == 0)
return pos;
- else if (d < 0)
+ else if (d > 0)
hi = pos - 1;
else
// This gets the insertion point right on the last loop
@@ -685,10 +685,10 @@ public class Collections
while (low <= hi)
{
pos = (low + hi) >> 1;
- final int d = compare(key, l.get(pos), c);
+ final int d = compare(l.get(pos), key, c);
if (d == 0)
return pos;
- else if (d < 0)
+ else if (d > 0)
hi = pos - 1;
else
// This gets the insertion point right on the last loop
diff --git a/libjava/classpath/java/util/Hashtable.java b/libjava/classpath/java/util/Hashtable.java
index 011cafaa855..76b0d5c15bd 100644
--- a/libjava/classpath/java/util/Hashtable.java
+++ b/libjava/classpath/java/util/Hashtable.java
@@ -1,6 +1,7 @@
/* 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.
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006
+ Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -110,12 +111,6 @@ public class Hashtable extends Dictionary
*/
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.
*/
@@ -302,7 +297,7 @@ public class Hashtable extends Dictionary
*/
public Enumeration keys()
{
- return new Enumerator(KEYS);
+ return new KeyEnumerator();
}
/**
@@ -316,7 +311,7 @@ public class Hashtable extends Dictionary
*/
public Enumeration elements()
{
- return new Enumerator(VALUES);
+ return new ValueEnumerator();
}
/**
@@ -333,20 +328,19 @@ public class Hashtable extends Dictionary
*/
public synchronized boolean contains(Object value)
{
+ if (value == null)
+ throw new NullPointerException();
+
for (int i = buckets.length - 1; i >= 0; i--)
{
HashEntry e = buckets[i];
while (e != null)
{
- if (value.equals(e.value))
+ if (e.value.equals(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;
}
@@ -385,7 +379,7 @@ public class Hashtable extends Dictionary
HashEntry e = buckets[idx];
while (e != null)
{
- if (key.equals(e.key))
+ if (e.key.equals(key))
return true;
e = e.next;
}
@@ -408,7 +402,7 @@ public class Hashtable extends Dictionary
HashEntry e = buckets[idx];
while (e != null)
{
- if (key.equals(e.key))
+ if (e.key.equals(key))
return e.value;
e = e.next;
}
@@ -438,7 +432,7 @@ public class Hashtable extends Dictionary
while (e != null)
{
- if (key.equals(e.key))
+ if (e.key.equals(key))
{
// Bypass e.setValue, since we already know value is non-null.
Object r = e.value;
@@ -484,7 +478,7 @@ public class Hashtable extends Dictionary
while (e != null)
{
- if (key.equals(e.key))
+ if (e.key.equals(key))
{
modCount++;
if (last == null)
@@ -581,8 +575,8 @@ public class Hashtable extends Dictionary
{
// 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);
+ // unsynchronized EntryIterator instead.
+ Iterator entries = new EntryIterator();
StringBuffer r = new StringBuffer("{");
for (int pos = size; pos > 0; pos--)
{
@@ -624,7 +618,7 @@ public class Hashtable extends Dictionary
public Iterator iterator()
{
- return new HashIterator(KEYS);
+ return new KeyIterator();
}
public void clear()
@@ -682,7 +676,7 @@ public class Hashtable extends Dictionary
public Iterator iterator()
{
- return new HashIterator(VALUES);
+ return new ValueIterator();
}
public void clear()
@@ -734,7 +728,7 @@ public class Hashtable extends Dictionary
public Iterator iterator()
{
- return new HashIterator(ENTRIES);
+ return new EntryIterator();
}
public void clear()
@@ -798,8 +792,8 @@ public class Hashtable extends Dictionary
{
// 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);
+ // unsynchronized EntryIterator instead.
+ Iterator itr = new EntryIterator();
int hashcode = 0;
for (int pos = size; pos > 0; pos--)
hashcode += itr.next().hashCode();
@@ -844,7 +838,7 @@ public class Hashtable extends Dictionary
HashEntry e = buckets[idx];
while (e != null)
{
- if (o.equals(e))
+ if (e.equals(o))
return e;
e = e.next;
}
@@ -904,8 +898,12 @@ public class Hashtable extends Dictionary
if (dest != null)
{
- while (dest.next != null)
- dest = dest.next;
+ HashEntry next = dest.next;
+ while (next != null)
+ {
+ dest = next;
+ next = dest.next;
+ }
dest.next = e;
}
else
@@ -940,8 +938,8 @@ public class Hashtable extends Dictionary
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);
+ // unsynchronized EntryIterator instead.
+ Iterator it = new EntryIterator();
while (it.hasNext())
{
HashEntry entry = (HashEntry) it.next();
@@ -980,22 +978,18 @@ public class Hashtable extends Dictionary
/**
* 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.
+ * This implementation iterates entries. Subclasses are used to
+ * iterate key and values. 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
+ * @author Fridjof Siebert
*/
- private final class HashIterator implements Iterator
+ private class EntryIterator 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;
@@ -1013,14 +1007,13 @@ public class Hashtable extends Dictionary
HashEntry next;
/**
- * Construct a new HashIterator with the supplied type.
- * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+ * Construct a new EtryIterator
*/
- HashIterator(int type)
+ EntryIterator()
{
- this.type = type;
}
+
/**
* Returns true if the Iterator has more elements.
* @return true if there are more elements
@@ -1049,14 +1042,13 @@ public class Hashtable extends Dictionary
HashEntry e = next;
while (e == null)
- e = buckets[--idx];
+ if (idx <= 0)
+ return null;
+ else
+ e = buckets[--idx];
next = e.next;
last = e;
- if (type == VALUES)
- return e.value;
- if (type == KEYS)
- return e.key;
return e;
}
@@ -1077,29 +1069,70 @@ public class Hashtable extends Dictionary
last = null;
knownMod++;
}
- } // class HashIterator
+ } // class EntryIterator
+
+ /**
+ * A class which implements the Iterator interface and is used for
+ * iterating over keys in Hashtables.
+ *
+ * @author Fridtjof Siebert
+ */
+ private class KeyIterator extends EntryIterator
+ {
+ /**
+ * 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()
+ {
+ return ((HashEntry)super.next()).key;
+ }
+ } // class KeyIterator
+
/**
- * 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.
+ * A class which implements the Iterator interface and is used for
+ * iterating over values in Hashtables.
+ *
+ * @author Fridtjof Siebert
+ */
+ private class ValueIterator extends EntryIterator
+ {
+ /**
+ * 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()
+ {
+ return ((HashEntry)super.next()).value;
+ }
+ } // class ValueIterator
+
+ /**
+ * Enumeration view of the entries in this Hashtable, providing
+ * sequential access to its elements.
*
* <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
+ * the "Java Class Libraries" book implies that modifications to the
* hashtable during enumeration causes indeterminate results. Don't do it!
*
* @author Jon Zeppieri
+ * @author Fridjof Siebert
*/
- private final class Enumerator implements Enumeration
+ private class EntryEnumerator 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. */
@@ -1113,11 +1146,10 @@ public class Hashtable extends Dictionary
/**
* Construct the enumeration.
- * @param type either {@link #KEYS} or {@link #VALUES}.
*/
- Enumerator(int type)
+ EntryEnumerator()
{
- this.type = type;
+ // Nothing to do here.
}
/**
@@ -1142,10 +1174,78 @@ public class Hashtable extends Dictionary
HashEntry e = next;
while (e == null)
- e = buckets[--idx];
+ if (idx <= 0)
+ return null;
+ else
+ e = buckets[--idx];
next = e.next;
- return type == VALUES ? e.value : e.key;
+ return e;
+ }
+ } // class EntryEnumerator
+
+
+ /**
+ * Enumeration view of this Hashtable, providing sequential access to its
+ * elements.
+ *
+ * <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 implies that modifications to the
+ * hashtable during enumeration causes indeterminate results. Don't do it!
+ *
+ * @author Jon Zeppieri
+ * @author Fridjof Siebert
+ */
+ private final class KeyEnumerator extends EntryEnumerator
+ {
+ /**
+ * Returns the next element.
+ * @return the next element
+ * @throws NoSuchElementException if there is none.
+ */
+ public Object nextElement()
+ {
+ HashEntry entry = (HashEntry) super.nextElement();
+ Object retVal = null;
+ if (entry != null)
+ retVal = entry.key;
+ return retVal;
}
- } // class Enumerator
+ } // class KeyEnumerator
+
+
+ /**
+ * Enumeration view of this Hashtable, providing sequential access to its
+ * values.
+ *
+ * <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 implies that modifications to the
+ * hashtable during enumeration causes indeterminate results. Don't do it!
+ *
+ * @author Jon Zeppieri
+ * @author Fridjof Siebert
+ */
+ private final class ValueEnumerator extends EntryEnumerator
+ {
+ /**
+ * Returns the next element.
+ * @return the next element
+ * @throws NoSuchElementException if there is none.
+ */
+ public Object nextElement()
+ {
+ HashEntry entry = (HashEntry) super.nextElement();
+ Object retVal = null;
+ if (entry != null)
+ retVal = entry.value;
+ return retVal;
+ }
+ } // class ValueEnumerator
+
} // class Hashtable
diff --git a/libjava/classpath/java/util/Properties.java b/libjava/classpath/java/util/Properties.java
index 7c468da8b4f..eb208f5a93d 100644
--- a/libjava/classpath/java/util/Properties.java
+++ b/libjava/classpath/java/util/Properties.java
@@ -47,15 +47,10 @@ import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.ext.DefaultHandler2;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
@@ -743,173 +738,64 @@ label = Name:\\u0020</pre>
throw new NullPointerException("Null input stream supplied.");
try
{
- SAXParserFactory factory = SAXParserFactory.newInstance();
- factory.setValidating(false); /* Don't use the URI */
- XMLReader parser = factory.newSAXParser().getXMLReader();
- PropertiesHandler handler = new PropertiesHandler();
- parser.setContentHandler(handler);
- parser.setProperty("http://xml.org/sax/properties/lexical-handler",
- handler);
- parser.parse(new InputSource(in));
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+ // Don't resolve external entity references
+ factory.setProperty("javax.xml.stream.isSupportingExternalEntities",
+ Boolean.FALSE);
+ XMLStreamReader reader = factory.createXMLStreamReader(in);
+ String name, key = null;
+ StringBuffer buf = null;
+ while (reader.hasNext())
+ {
+ switch (reader.next())
+ {
+ case XMLStreamConstants.START_ELEMENT:
+ name = reader.getLocalName();
+ if (buf == null && "entry".equals(name))
+ {
+ key = reader.getAttributeValue(null, "key");
+ if (key == null)
+ {
+ String msg = "missing 'key' attribute";
+ throw new InvalidPropertiesFormatException(msg);
+ }
+ buf = new StringBuffer();
+ }
+ else if (!"properties".equals(name) && !"comment".equals(name))
+ {
+ String msg = "unexpected element name '" + name + "'";
+ throw new InvalidPropertiesFormatException(msg);
+ }
+ break;
+ case XMLStreamConstants.END_ELEMENT:
+ name = reader.getLocalName();
+ if (buf != null && "entry".equals(name))
+ {
+ put(key, buf.toString());
+ buf = null;
+ }
+ else if (!"properties".equals(name) && !"comment".equals(name))
+ {
+ String msg = "unexpected element name '" + name + "'";
+ throw new InvalidPropertiesFormatException(msg);
+ }
+ break;
+ case XMLStreamConstants.CHARACTERS:
+ case XMLStreamConstants.SPACE:
+ case XMLStreamConstants.CDATA:
+ if (buf != null)
+ buf.append(reader.getText());
+ break;
+ }
+ }
+ reader.close();
}
- catch (SAXException e)
+ catch (XMLStreamException e)
{
throw (InvalidPropertiesFormatException)
new InvalidPropertiesFormatException("Error in parsing XML.").
initCause(e);
}
- catch (ParserConfigurationException e)
- {
- throw (IOException)
- new IOException("An XML parser could not be found.").
- initCause(e);
- }
}
- /**
- * This class deals with the parsing of XML using
- * <a href="http://java.sun.com/dtd/properties.dtd">
- * http://java.sun.com/dtd/properties.dtd</a>.
- *
- * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
- * @since 1.5
- */
- private class PropertiesHandler
- extends DefaultHandler2
- {
-
- /**
- * The current key.
- */
- private String key;
-
- /**
- * The current value.
- */
- private String value;
-
- /**
- * A flag to check whether a valid DTD declaration has been seen.
- */
- private boolean dtdDeclSeen;
-
- /**
- * Constructs a new Properties handler.
- */
- public PropertiesHandler()
- {
- key = null;
- value = null;
- dtdDeclSeen = false;
- }
-
- /**
- * <p>
- * Captures the start of the DTD declarations, if they exist.
- * A valid properties file must declare the following doctype:
- * </p>
- * <p>
- * <code>!DOCTYPE properties SYSTEM
- * "http://java.sun.com/dtd/properties.dtd"</code>
- * </p>
- *
- * @param name the name of the document type.
- * @param publicId the public identifier that was declared, or
- * null if there wasn't one.
- * @param systemId the system identifier that was declared, or
- * null if there wasn't one.
- * @throws SAXException if some error occurs in parsing.
- */
- public void startDTD(String name, String publicId, String systemId)
- throws SAXException
- {
- if (name.equals("properties") &&
- publicId == null &&
- systemId.equals("http://java.sun.com/dtd/properties.dtd"))
- {
- dtdDeclSeen = true;
- }
- else
- throw new SAXException("Invalid DTD declaration: " + name);
- }
-
- /**
- * Captures the start of an XML element.
- *
- * @param uri the namespace URI.
- * @param localName the local name of the element inside the namespace.
- * @param qName the local name qualified with the namespace URI.
- * @param attributes the attributes of this element.
- * @throws SAXException if some error occurs in parsing.
- */
- public void startElement(String uri, String localName,
- String qName, Attributes attributes)
- throws SAXException
- {
- if (qName.equals("entry"))
- {
- int index = attributes.getIndex("key");
- if (index != -1)
- key = attributes.getValue(index);
- }
- else if (qName.equals("comment") || qName.equals("properties"))
- {
- /* Ignore it */
- }
- else
- throw new SAXException("Invalid tag: " + qName);
- }
-
- /**
- * Captures characters within an XML element.
- *
- * @param ch the array of characters.
- * @param start the start index of the characters to use.
- * @param length the number of characters to use from the start index on.
- * @throws SAXException if some error occurs in parsing.
- */
- public void characters(char[] ch, int start, int length)
- throws SAXException
- {
- if (key != null)
- value = new String(ch,start,length);
- }
-
- /**
- * Captures the end of an XML element.
- *
- * @param uri the namespace URI.
- * @param localName the local name of the element inside the namespace.
- * @param qName the local name qualified with the namespace URI.
- * @throws SAXException if some error occurs in parsing.
- */
- public void endElement(String uri, String localName,
- String qName)
- throws SAXException
- {
- if (qName.equals("entry"))
- {
- if (value == null)
- value = "";
- setProperty(key, value);
- key = null;
- value = null;
- }
- }
-
- /**
- * Captures the end of the XML document. If a DTD declaration has
- * not been seen, the document is erroneous and an exception is thrown.
- *
- * @throws SAXException if the correct DTD declaration didn't appear.
- */
- public void endDocument()
- throws SAXException
- {
- if (!dtdDeclSeen)
- throw new SAXException("No appropriate DTD declaration was seen.");
- }
-
- } // class PropertiesHandler
-
} // class Properties
diff --git a/libjava/classpath/java/util/StringTokenizer.java b/libjava/classpath/java/util/StringTokenizer.java
index dcc192c855a..21298c75ec7 100644
--- a/libjava/classpath/java/util/StringTokenizer.java
+++ b/libjava/classpath/java/util/StringTokenizer.java
@@ -132,8 +132,7 @@ public class StringTokenizer implements Enumeration
{
len = str.length();
this.str = str;
- // The toString() hack causes the NullPointerException.
- this.delim = delim.toString();
+ this.delim = delim;
this.retDelims = returnDelims;
this.pos = 0;
}
diff --git a/libjava/classpath/java/util/WeakHashMap.java b/libjava/classpath/java/util/WeakHashMap.java
index 514ad8cd29f..2ed982ac32d 100644
--- a/libjava/classpath/java/util/WeakHashMap.java
+++ b/libjava/classpath/java/util/WeakHashMap.java
@@ -475,7 +475,7 @@ public class WeakHashMap extends AbstractMap implements Map
if (o instanceof Map.Entry)
{
Map.Entry e = (Map.Entry) o;
- return key.equals(e.getKey())
+ return WeakHashMap.equals(getKey(), e.getKey())
&& WeakHashMap.equals(value, e.getValue());
}
return false;
@@ -483,7 +483,7 @@ public class WeakHashMap extends AbstractMap implements Map
public String toString()
{
- return key + "=" + value;
+ return getKey() + "=" + value;
}
}
@@ -657,7 +657,7 @@ public class WeakHashMap extends AbstractMap implements Map
while (bucket != null)
{
WeakBucket.WeakEntry entry = bucket.getEntry();
- if (entry != null && key.equals(entry.key))
+ if (entry != null && equals(key, entry.key))
return entry;
bucket = bucket.next;
diff --git a/libjava/classpath/java/util/logging/XMLFormatter.java b/libjava/classpath/java/util/logging/XMLFormatter.java
index 8bd83ba3973..8f5769be1b9 100644
--- a/libjava/classpath/java/util/logging/XMLFormatter.java
+++ b/libjava/classpath/java/util/logging/XMLFormatter.java
@@ -194,7 +194,7 @@ public class XMLFormatter
appendTag(buf, 1, "date", iso8601.format(new Date(millis)));
- appendTag(buf, 1, "millis", record.getMillis());
+ appendTag(buf, 1, "millis", millis);
appendTag(buf, 1, "sequence", record.getSequenceNumber());
appendTag(buf, 1, "logger", record.getLoggerName());
diff --git a/libjava/classpath/java/util/regex/Pattern.java b/libjava/classpath/java/util/regex/Pattern.java
index 6a31ef9bfd2..d39f1cfb04d 100644
--- a/libjava/classpath/java/util/regex/Pattern.java
+++ b/libjava/classpath/java/util/regex/Pattern.java
@@ -103,8 +103,11 @@ public final class Pattern implements Serializable
}
catch (REException e)
{
- throw new PatternSyntaxException(e.getMessage(),
+ PatternSyntaxException pse;
+ pse = new PatternSyntaxException(e.getMessage(),
regex, e.getPosition());
+ pse.initCause(e);
+ throw pse;
}
}