summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
authorAndrew John Hughes <gnu_andrew@member.fsf.org>2006-01-10 15:59:36 +0000
committerAndrew John Hughes <gnu_andrew@member.fsf.org>2006-01-10 15:59:36 +0000
commite2880df85352a4f46c1fc8b28c4cd08540a2daed (patch)
treeaee81230bdbf73bd817662e0ce7a4665939ab41d /java
parent05f169147352fa3eeb9726f3d57597a83eb7d64d (diff)
downloadclasspath-e2880df85352a4f46c1fc8b28c4cd08540a2daed.tar.gz
2006-01-10 Andrew John Hughes <gnu_andrew@member.fsf.org>
Merge of HEAD --> generics branch for the period 2005/11/27 to 2006/01/09.
Diffstat (limited to 'java')
-rw-r--r--java/awt/BorderLayout.java2
-rw-r--r--java/awt/Component.java10
-rw-r--r--java/awt/Container.java34
-rw-r--r--java/awt/GridBagLayout.java24
-rw-r--r--java/awt/datatransfer/SystemFlavorMap.java263
-rw-r--r--java/beans/DefaultPersistenceDelegate.java194
-rw-r--r--java/beans/Encoder.java463
-rw-r--r--java/beans/Expression.java81
-rw-r--r--java/beans/PersistenceDelegate.java91
-rw-r--r--java/beans/PropertyChangeSupport.java9
-rw-r--r--java/beans/Statement.java165
-rw-r--r--java/beans/XMLEncoder.java265
-rw-r--r--java/io/File.java9
-rw-r--r--java/io/InputStreamReader.java4
-rw-r--r--java/io/ObjectInputStream.java1
-rw-r--r--java/io/ObjectOutputStream.java5
-rw-r--r--java/io/ObjectStreamClass.java31
-rw-r--r--java/io/OutputStreamWriter.java4
-rw-r--r--java/io/RandomAccessFile.java47
-rw-r--r--java/lang/Character.java11
-rw-r--r--java/lang/Class.java8
-rw-r--r--java/lang/Double.java75
-rw-r--r--java/lang/Float.java77
-rw-r--r--java/lang/InheritableThreadLocal.java14
-rw-r--r--java/lang/String.java16
-rw-r--r--java/lang/Thread.java6
-rw-r--r--java/lang/ThreadLocal.java35
-rw-r--r--java/net/DatagramSocket.java7
-rw-r--r--java/net/InetAddress.java6
-rw-r--r--java/net/Socket.java20
-rw-r--r--java/net/URL.java2
-rw-r--r--java/nio/charset/Charset.java2
-rw-r--r--java/security/MessageDigest.java5
-rw-r--r--java/security/Security.java17
-rw-r--r--java/text/Bidi.java78
-rw-r--r--java/text/DecimalFormat.java4
-rw-r--r--java/util/Collections.java8
-rw-r--r--java/util/Properties.java226
-rw-r--r--java/util/logging/XMLFormatter.java2
39 files changed, 1897 insertions, 424 deletions
diff --git a/java/awt/BorderLayout.java b/java/awt/BorderLayout.java
index 1b67c01cf..7c8c582a9 100644
--- a/java/awt/BorderLayout.java
+++ b/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/java/awt/Component.java b/java/awt/Component.java
index dfc49f386..7d4346582 100644
--- a/java/awt/Component.java
+++ b/java/awt/Component.java
@@ -4792,7 +4792,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);
@@ -4823,7 +4828,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);
}
diff --git a/java/awt/Container.java b/java/awt/Container.java
index 35c281a65..6d4a34571 100644
--- a/java/awt/Container.java
+++ b/java/awt/Container.java
@@ -451,9 +451,6 @@ public class Container extends Component
ContainerEvent.COMPONENT_REMOVED,
r);
getToolkit().getSystemEventQueue().postEvent(ce);
-
- // Repaint this container.
- repaint();
}
}
}
@@ -1638,30 +1635,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();
}
}
@@ -2161,12 +2147,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/java/awt/GridBagLayout.java b/java/awt/GridBagLayout.java
index 7e8fa0fb6..959a1206d 100644
--- a/java/awt/GridBagLayout.java
+++ b/java/awt/GridBagLayout.java
@@ -342,11 +342,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];
@@ -358,11 +361,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;
@@ -439,11 +442,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/java/awt/datatransfer/SystemFlavorMap.java b/java/awt/datatransfer/SystemFlavorMap.java
index 702830789..1b5a69d0a 100644
--- a/java/awt/datatransfer/SystemFlavorMap.java
+++ b/java/awt/datatransfer/SystemFlavorMap.java
@@ -38,6 +38,7 @@ 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;
@@ -59,7 +60,24 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable
* <code>SystemFlavorMaps</code>.
*/
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.
*/
@@ -109,7 +127,7 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable
.getContextClassLoader();
//if ContextClassLoader not set, use system default
- if(classLoader == null)
+ if (classLoader == null)
{
classLoader = ClassLoader.getSystemClassLoader();
}
@@ -118,7 +136,7 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable
{
FlavorMap map = (FlavorMap)
systemFlavorMaps.get(classLoader);
- if(map == null)
+ if (map == null)
{
map = new SystemFlavorMap();
systemFlavorMaps.put(classLoader, map);
@@ -128,36 +146,90 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable
}
/**
- * 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;
}
/**
@@ -175,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<DataFlavor> getFlavorsForNative (String nat)
{
throw new Error ("Not implemented");
@@ -184,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/java/beans/DefaultPersistenceDelegate.java b/java/beans/DefaultPersistenceDelegate.java
new file mode 100644
index 000000000..9dd1ae564
--- /dev/null
+++ b/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/java/beans/Encoder.java b/java/beans/Encoder.java
new file mode 100644
index 000000000..9b96aaa8e
--- /dev/null
+++ b/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/java/beans/Expression.java b/java/beans/Expression.java
index d92cb7284..b327864d9 100644
--- a/java/beans/Expression.java
+++ b/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/java/beans/PersistenceDelegate.java b/java/beans/PersistenceDelegate.java
new file mode 100644
index 000000000..b33cbcbed
--- /dev/null
+++ b/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/java/beans/PropertyChangeSupport.java b/java/beans/PropertyChangeSupport.java
index da30f994d..991390b56 100644
--- a/java/beans/PropertyChangeSupport.java
+++ b/java/beans/PropertyChangeSupport.java
@@ -408,11 +408,10 @@ public class PropertyChangeSupport implements Serializable
public void fireIndexedPropertyChange(String name, int index,
Object oldValue, Object newValue)
{
- // FIXME: should we use equals() here?
- if (oldValue == newValue && oldValue != null)
- firePropertyChange(new IndexedPropertyChangeEvent(source, name,
- oldValue, newValue,
- index));
+ // Argument checking is done in firePropertyChange(PropertyChangeEvent) .
+ firePropertyChange(new IndexedPropertyChangeEvent(source, name,
+ oldValue, newValue,
+ index));
}
/**
diff --git a/java/beans/Statement.java b/java/beans/Statement.java
index 8e916a286..62a5ad7b6 100644
--- a/java/beans/Statement.java
+++ b/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/java/beans/XMLEncoder.java b/java/beans/XMLEncoder.java
new file mode 100644
index 000000000..f9cbe6396
--- /dev/null
+++ b/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/java/io/File.java b/java/io/File.java
index 6af262943..731fa5826 100644
--- a/java/io/File.java
+++ b/java/io/File.java
@@ -396,7 +396,8 @@ public class File implements Serializable, Comparable<File>
* 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<File>
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/java/io/InputStreamReader.java b/java/io/InputStreamReader.java
index 57cdc53ed..ef8fd4542 100644
--- a/java/io/InputStreamReader.java
+++ b/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/java/io/ObjectInputStream.java b/java/io/ObjectInputStream.java
index b8c8b79a8..3fff967b4 100644
--- a/java/io/ObjectInputStream.java
+++ b/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/java/io/ObjectOutputStream.java b/java/io/ObjectOutputStream.java
index 2a63d4980..628d0e2b4 100644
--- a/java/io/ObjectOutputStream.java
+++ b/java/io/ObjectOutputStream.java
@@ -448,6 +448,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/java/io/ObjectStreamClass.java b/java/io/ObjectStreamClass.java
index f55a978d5..1df7c7ea0 100644
--- a/java/io/ObjectStreamClass.java
+++ b/java/io/ObjectStreamClass.java
@@ -70,6 +70,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>,
@@ -78,6 +80,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)
@@ -155,6 +162,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.
*/
@@ -615,6 +624,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/java/io/OutputStreamWriter.java b/java/io/OutputStreamWriter.java
index 29fb631fa..572683834 100644
--- a/java/io/OutputStreamWriter.java
+++ b/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/java/io/RandomAccessFile.java b/java/io/RandomAccessFile.java
index 048b3f3b2..d719a1e3b 100644
--- a/java/io/RandomAccessFile.java
+++ b/java/io/RandomAccessFile.java
@@ -124,7 +124,10 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
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, Closeable
*/
public final void writeUTF (String val) throws IOException
{
+ if (out == null)
+ throw new IOException("Bad file descriptor");
+
out.writeUTF(val);
}
diff --git a/java/lang/Character.java b/java/lang/Character.java
index e33c15585..b0535e8b6 100644
--- a/java/lang/Character.java
+++ b/java/lang/Character.java
@@ -3054,11 +3054,11 @@ public final class Character implements Serializable, Comparable<Character>
{
// 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;
@@ -3167,7 +3167,8 @@ public final class Character implements Serializable, Comparable<Character>
*/
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/java/lang/Class.java b/java/lang/Class.java
index 82d47819b..af5f3c723 100644
--- a/java/lang/Class.java
+++ b/java/lang/Class.java
@@ -590,8 +590,7 @@ public final class Class<T>
/**
* 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
@@ -843,7 +842,10 @@ public final class Class<T>
*/
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/java/lang/Double.java b/java/lang/Double.java
index f79432a99..c71620314 100644
--- a/java/lang/Double.java
+++ b/java/lang/Double.java
@@ -175,6 +175,81 @@ public final class Double extends Number implements Comparable<Double>
}
/**
+ * 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/java/lang/Float.java b/java/lang/Float.java
index 56cc8e136..1e85922be 100644
--- a/java/lang/Float.java
+++ b/java/lang/Float.java
@@ -185,6 +185,83 @@ public final class Float extends Number implements Comparable<Float>
}
/**
+ * 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/java/lang/InheritableThreadLocal.java b/java/lang/InheritableThreadLocal.java
index 9d02e921d..bbcbc0c2f 100644
--- a/java/lang/InheritableThreadLocal.java
+++ b/java/lang/InheritableThreadLocal.java
@@ -1,5 +1,5 @@
/* InheritableThreadLocal -- a ThreadLocal which inherits values across threads
- Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2000, 2001, 2002, 2003, 2004, 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
@@ -101,16 +102,15 @@ public class InheritableThreadLocal<T> extends ThreadLocal<T>
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/java/lang/String.java b/java/lang/String.java
index fdec7f929..c3c92a178 100644
--- a/java/lang/String.java
+++ b/java/lang/String.java
@@ -277,7 +277,8 @@ public final class String
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];
@@ -342,7 +343,8 @@ public final class String
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
@@ -422,7 +424,8 @@ public final class String
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;
@@ -537,7 +540,8 @@ public final class String
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)
@@ -1763,7 +1767,7 @@ public final class String
/**
* 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.
@@ -1881,6 +1885,8 @@ public final class String
* 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/java/lang/Thread.java b/java/lang/Thread.java
index 5430facf5..e64402610 100644
--- a/java/lang/Thread.java
+++ b/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
@@ -141,7 +141,7 @@ public class Thread implements Runnable
/** Thread local storage. Package accessible for use by
* InheritableThreadLocal.
*/
- WeakHashMap locals;
+ WeakIdentityHashMap locals;
/** The uncaught exception handler. */
UncaughtExceptionHandler exceptionHandler;
@@ -1015,7 +1015,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/java/lang/ThreadLocal.java b/java/lang/ThreadLocal.java
index 670c13a03..f599cfb44 100644
--- a/java/lang/ThreadLocal.java
+++ b/java/lang/ThreadLocal.java
@@ -1,5 +1,5 @@
/* ThreadLocal -- a variable with a unique value per thread
- Copyright (C) 2000, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2000, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -96,29 +96,6 @@ public class ThreadLocal<T>
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(this);
-
- static class Key
- {
- private ThreadLocal outer;
-
- Key(ThreadLocal outer)
- {
- this.outer = outer;
- }
-
- ThreadLocal get()
- {
- return outer;
- }
- }
-
-
- /**
* Creates a ThreadLocal object without associating any value to it yet.
*/
public ThreadLocal()
@@ -148,14 +125,14 @@ public class ThreadLocal<T>
*/
public T get()
{
- Map<Key,T> map = (Map<Key,T>) Thread.getThreadLocals();
+ Map<ThreadLocal<T>,T> map = (Map<ThreadLocal<T>,T>) Thread.getThreadLocals();
// Note that we don't have to synchronize, as only this thread will
// ever modify the map.
- T value = map.get(key);
+ T value = map.get(this);
if (value == null)
{
value = initialValue();
- map.put(key, value == null ? (T) NULL : value);
+ map.put(this, (T) (value == null ? NULL : value));
}
return value == (T) NULL ? null : value;
}
@@ -173,7 +150,7 @@ public class ThreadLocal<T>
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);
}
/**
@@ -184,6 +161,6 @@ public class ThreadLocal<T>
public void remove()
{
Map map = Thread.getThreadLocals();
- map.remove(key);
+ map.remove(this);
}
}
diff --git a/java/net/DatagramSocket.java b/java/net/DatagramSocket.java
index 40bafbb34..d8837c006 100644
--- a/java/net/DatagramSocket.java
+++ b/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/java/net/InetAddress.java b/java/net/InetAddress.java
index 94dc6cb6d..89d46afb7 100644
--- a/java/net/InetAddress.java
+++ b/java/net/InetAddress.java
@@ -649,8 +649,11 @@ 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;
@@ -757,6 +760,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/java/net/Socket.java b/java/net/Socket.java
index 0ff6e6ea0..b2249ffaa 100644
--- a/java/net/Socket.java
+++ b/java/net/Socket.java
@@ -437,25 +437,7 @@ public class Socket
if (! isBound())
bind(null);
- try
- {
- getImpl().connect(endpoint, timeout);
- }
- catch (IOException exception)
- {
- close();
- throw exception;
- }
- catch (RuntimeException exception)
- {
- close();
- throw exception;
- }
- catch (Error error)
- {
- close();
- throw error;
- }
+ getImpl().connect(endpoint, timeout);
}
/**
diff --git a/java/net/URL.java b/java/net/URL.java
index 1d947a0b4..fdd6a46b4 100644
--- a/java/net/URL.java
+++ b/java/net/URL.java
@@ -342,7 +342,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);
}
/**
diff --git a/java/nio/charset/Charset.java b/java/nio/charset/Charset.java
index 098482c32..556e4707e 100644
--- a/java/nio/charset/Charset.java
+++ b/java/nio/charset/Charset.java
@@ -121,6 +121,8 @@ public abstract class Charset implements Comparable<Charset>
*
* This may be set by the user or VM with the file.encoding
* property.
+ *
+ * @since 1.5
*/
public static Charset defaultCharset()
{
diff --git a/java/security/MessageDigest.java b/java/security/MessageDigest.java
index 8684f2083..8a6af645b 100644
--- a/java/security/MessageDigest.java
+++ b/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/java/security/Security.java b/java/security/Security.java
index c5ef9f97c..6266300c0 100644
--- a/java/security/Security.java
+++ b/java/security/Security.java
@@ -354,6 +354,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++)
@@ -399,20 +407,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/java/text/Bidi.java b/java/text/Bidi.java
new file mode 100644
index 000000000..57b9a88df
--- /dev/null
+++ b/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/java/text/DecimalFormat.java b/java/text/DecimalFormat.java
index 6dadb0ce3..a9ec7767f 100644
--- a/java/text/DecimalFormat.java
+++ b/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/java/util/Collections.java b/java/util/Collections.java
index 993a42ff5..6cf21a53b 100644
--- a/java/util/Collections.java
+++ b/java/util/Collections.java
@@ -716,10 +716,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
@@ -731,10 +731,10 @@ public class Collections
while (low <= hi)
{
pos = (low + hi) >> 1;
- final int d = compare(key, ((List<T>) l).get(pos), c);
+ final int d = compare(((List<T>) 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/java/util/Properties.java b/java/util/Properties.java
index 703869fa4..e294fee7e 100644
--- a/java/util/Properties.java
+++ b/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;
@@ -744,173 +739,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/java/util/logging/XMLFormatter.java b/java/util/logging/XMLFormatter.java
index 8bd83ba39..8f5769be1 100644
--- a/java/util/logging/XMLFormatter.java
+++ b/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());