diff options
author | doko <doko@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-06-03 23:18:43 +0000 |
---|---|---|
committer | doko <doko@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-06-03 23:18:43 +0000 |
commit | 5bf762459121cc397663d22498d62d71fa179ef6 (patch) | |
tree | a9c9e7d91c484d53fe154f9285fc57325572ce50 /libjava/classpath/java | |
parent | 6d7301dc346a198a89ac987c1008aac09f191ee6 (diff) | |
download | gcc-5bf762459121cc397663d22498d62d71fa179ef6.tar.gz |
libjava/classpath/ChangeLog.gcj:
2007-05-31 Matthias Klose <doko@ubuntu.com>
* javax/management/NotificationBroadcasterSupport.java
(getNotificationInfo): Add cast.
* native/jni/qt-peer/Makefile.am (AM_CXXFLAGS): Add libstdc++ include
directories.
* native/jni/qt-peer/Makefile.in: Regenerate.
libjava/ChangeLog:
2007-06-03 Matthias Klose <doko@ubuntu.com>
* java/io/natFileWin32.cc (setFilePermissions): New (stub only).
_access: Handle EXEC query, stub only.
2007-06-03 Matthias Klose <doko@ubuntu.com>
Merged from classpath:
* gnu/java/nio/SelectorProviderImpl.java: Whitespace merge.
* java/lang/System.java(inheritedChannel): New.
* java/lang/Character.java: Remove stray`;'.
* java/net/MulticastSocket.java: Merged.
* java/text/DateFormatSymbols.java(getInstance): New, comment updates.
* java/text/Collator.java(getInstance): Merged.
* java/util/Calendar.java: New attributes ALL_STYLES, SHORT, LONG.
getDisplayName, getDisplayNames: New.
* java/util/logging/Logger.java: Merged.
* Regenerate .class and .h files.
2007-06-03 Matthias Klose <doko@ubuntu.com>
* java/io/File.java: Merge with classpath-0.95, new method
setFilePermissions, new attribute EXEC.
* java/io/natFilePosix.cc (setFilePermissions): New.
_access: Handle EXEC query.
* classpath/lib/java/io/File.class, java/io/File.h: Regenerate.
2007-06-03 Matthias Klose <doko@ubuntu.com>
Imported GNU Classpath 0.95.
* classpath/Makefile.in,
classpath/native/jni/midi-dssi/Makefile.in,
classpath/native/jni/classpath/Makefile.in,
classpath/native/jni/Makefile.in,
classpath/native/jni/gconf-peer/Makefile.in,
classpath/native/jni/java-io/Makefile.in,
classpath/native/jni/native-lib/Makefile.in,
classpath/native/jni/java-util/Makefile.in,
classpath/native/jni/midi-alsa/Makefile.in,
classpath/native/jni/java-lang/Makefile.in,
classpath/native/jni/java-nio/Makefile.in,
classpath/native/jni/java-net/Makefile.in,
classpath/native/jni/xmlj/Makefile.in,
classpath/native/jni/qt-peer/Makefile.in,
classpath/native/jni/gtk-peer/Makefile.in,
classpath/native/Makefile.in, classpath/native/jawt/Makefile.in,
classpath/native/fdlibm/Makefile.in,
classpath/native/plugin/Makefile.in,
classpath/resource/Makefile.in, classpath/scripts/Makefile.in,
classpath/tools/Makefile.in, classpath/doc/Makefile.in,
classpath/doc/api/Makefile.in, classpath/lib/Makefile.in,
classpath/external/Makefile.in, classpath/external/jsr166/Makefile.in,
classpath/external/sax/Makefile.in,
classpath/external/w3c_dom/Makefile.in,
classpath/external/relaxngDatatype/Makefile.in,
classpath/include/Makefile.in,
classpath/examples/Makefile.in: Regenerate.
* classpath/config.guess, classpath/config.sub,
classpath/ltmain.sh : Update.
* classpath/configure, classpath/depcomp, classpath/missing,
classpath/aclocal.m4, classpath/install-sh: Regenerate.
* gnu/classpath/Configuration.java (CLASSPATH_VERSION): Now 0.95.
* sources.am: Regenerate.
* Makefile.in: Regenerate.
* Update the .class files and generated CNI header files, add new
.class and generated CNI header files.
* Remove generated files for removed java source files:
classpath/gnu/java/net/BASE64.java,
classpath/gnu/java/security/util/Base64.java,
classpath/gnu/java/awt/peer/gtk/GThreadMutex.java,
classpath/gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java,
classpath/gnu/java/awt/font/autofit/Scaler.java,
classpath/gnu/classpath/jdwp/util/Value.java,
classpath/gnu/javax/net/ssl/Base64.java.
* Remove empty directories.
* Makefile.am(nat_source_files): Add natVMOperatingSystemMXBeanImpl.cc.
* java/lang/Class.java(setAccessible): Merge from classpath.
* java/util/Locale.java: Remove.
* gnu/java/lang/management/VMOperatingSystemMXBeanImpl.java,
gnu/java/lang/management/natVMOperatingSystemMXBeanImpl.cc: New.
* gcj/javaprims.h: Update class declarations.
* scripts/classes.pl: Update usage.
* HACKING: Mention to build all peers.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@125302 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/java')
116 files changed, 9783 insertions, 2713 deletions
diff --git a/libjava/classpath/java/awt/AWTEvent.java b/libjava/classpath/java/awt/AWTEvent.java index 3f4027c2c05..102062cdfe6 100644 --- a/libjava/classpath/java/awt/AWTEvent.java +++ b/libjava/classpath/java/awt/AWTEvent.java @@ -262,9 +262,17 @@ public abstract class AWTEvent extends EventObject */ public String toString () { + String src; + if (source instanceof Component) + src = ((Component) source).getName(); + else if (source instanceof MenuComponent) + src = ((MenuComponent) source).getName(); + else if (source != null) + src = source.toString(); + else + src = "null"; String string = getClass ().getName () + "[" + paramString () + "] on " - + source; - + + src; return string; } diff --git a/libjava/classpath/java/awt/AWTKeyStroke.java b/libjava/classpath/java/awt/AWTKeyStroke.java index 527e85873e0..e0b34e99238 100644 --- a/libjava/classpath/java/awt/AWTKeyStroke.java +++ b/libjava/classpath/java/awt/AWTKeyStroke.java @@ -1,5 +1,5 @@ /* AWTKeyStroke.java -- an immutable key stroke - Copyright (C) 2002, 2004, 2005 Free Software Foundation + Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -93,9 +93,9 @@ public class AWTKeyStroke implements Serializable private static final int MAX_CACHE_SIZE = 2048; /** Prune stale entries. */ - protected boolean removeEldestEntry(Map.Entry<AWTKeyStroke,AWTKeyStroke> + protected boolean removeEldestEntry(Entry<AWTKeyStroke,AWTKeyStroke> eldest) - { // XXX - FIXME Use Map.Entry, not just Entry as gcj 3.1 workaround. + { return size() > MAX_CACHE_SIZE; } }; diff --git a/libjava/classpath/java/awt/AlphaComposite.java b/libjava/classpath/java/awt/AlphaComposite.java index 92b9e09a60d..90df2e66d8c 100644 --- a/libjava/classpath/java/awt/AlphaComposite.java +++ b/libjava/classpath/java/awt/AlphaComposite.java @@ -1,5 +1,5 @@ /* AlphaComposite.java -- provides a context for performing alpha compositing - Copyright (C) 2002, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -61,8 +61,8 @@ public final class AlphaComposite implements Composite private static final int MAX_CACHE_SIZE = 2048; /** Prune stale entries. */ - protected boolean removeEldestEntry(Map.Entry eldest) - { // XXX - FIXME Use Map.Entry, not just Entry as gcj 3.1 workaround. + protected boolean removeEldestEntry(Entry eldest) + { return size() > MAX_CACHE_SIZE; } }; diff --git a/libjava/classpath/java/awt/Canvas.java b/libjava/classpath/java/awt/Canvas.java index 843fded44db..95db1f57e1a 100644 --- a/libjava/classpath/java/awt/Canvas.java +++ b/libjava/classpath/java/awt/Canvas.java @@ -75,11 +75,6 @@ public class Canvas private static transient long next_canvas_number; /** - * The graphics configuration associated with the canvas. - */ - transient GraphicsConfiguration graphicsConfiguration; - - /** * The buffer strategy associated with this canvas. */ transient BufferStrategy bufferStrategy; @@ -100,14 +95,7 @@ public class Canvas */ public Canvas(GraphicsConfiguration graphicsConfiguration) { - this.graphicsConfiguration = graphicsConfiguration; - } - - GraphicsConfiguration getGraphicsConfigurationImpl() - { - if (graphicsConfiguration != null) - return graphicsConfiguration; - return super.getGraphicsConfigurationImpl(); + this.graphicsConfig = graphicsConfiguration; } /** diff --git a/libjava/classpath/java/awt/CardLayout.java b/libjava/classpath/java/awt/CardLayout.java index 2e3feece8d2..35380d2370b 100644 --- a/libjava/classpath/java/awt/CardLayout.java +++ b/libjava/classpath/java/awt/CardLayout.java @@ -225,7 +225,7 @@ public class CardLayout implements LayoutManager2, Serializable */ public Dimension maximumLayoutSize (Container target) { - if (target == null) + if (target == null || target.ncomponents == 0) return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); // The JCL says that this returns Integer.MAX_VALUE for both // dimensions. But that just seems wrong to me. diff --git a/libjava/classpath/java/awt/Component.java b/libjava/classpath/java/awt/Component.java index b6eadabbb5c..f8bed17618e 100644 --- a/libjava/classpath/java/awt/Component.java +++ b/libjava/classpath/java/awt/Component.java @@ -726,7 +726,23 @@ public abstract class Component */ public GraphicsConfiguration getGraphicsConfiguration() { - return getGraphicsConfigurationImpl(); + GraphicsConfiguration conf = null; + synchronized (getTreeLock()) + { + if (graphicsConfig != null) + { + conf = graphicsConfig; + } + else + { + Component par = getParent(); + if (par != null) + { + conf = parent.getGraphicsConfiguration(); + } + } + } + return conf; } /** @@ -823,7 +839,8 @@ public abstract class Component */ public boolean isShowing() { - return visible && peer != null && (parent == null || parent.isShowing()); + Component par = parent; + return visible && peer != null && (par == null || par.isShowing()); } /** @@ -1179,19 +1196,12 @@ public abstract class Component */ public Font getFont() { - Font f; - synchronized (getTreeLock()) - { - f = getFontImpl(); - } - return f; + return getFontImpl(); } /** * Implementation of getFont(). This is pulled out of getFont() to prevent - * client programs from overriding this. This method is executed within - * a tree lock, so we can assume that the hierarchy doesn't change in - * between. + * client programs from overriding this. * * @return the font of this component */ @@ -1204,7 +1214,12 @@ public abstract class Component if (p != null) f = p.getFontImpl(); else - f = new Font("Dialog", Font.PLAIN, 12); + { + // It is important to return null here and not some kind of default + // font, otherwise the Swing UI would not install its fonts because + // it keeps non-UIResource fonts. + f = null; + } } return f; } @@ -5435,27 +5450,6 @@ p * <li>the set of backward traversal keys } /** - * Implementation method that allows classes such as Canvas and Window to - * override the graphics configuration without violating the published API. - * - * @return the graphics configuration - */ - GraphicsConfiguration getGraphicsConfigurationImpl() - { - if (peer != null) - { - GraphicsConfiguration config = peer.getGraphicsConfiguration(); - if (config != null) - return config; - } - - if (parent != null) - return parent.getGraphicsConfiguration(); - - return null; - } - - /** * Translate an AWT 1.1 event ({@link AWTEvent}) into an AWT 1.0 * event ({@link Event}). * diff --git a/libjava/classpath/java/awt/Desktop.java b/libjava/classpath/java/awt/Desktop.java new file mode 100644 index 00000000000..8010464c00d --- /dev/null +++ b/libjava/classpath/java/awt/Desktop.java @@ -0,0 +1,268 @@ +/* Desktop.java -- enable desktop integration between java programs and system + programs. + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.DesktopPeer; +import java.io.File; +import java.io.IOException; +import java.net.URI; + +/** + * This class enables Java application to access system commands to perform + * desktop oriented operations, like writing and sending emails, or surfing + * webpages with the system browser or editing/printing files with a default + * editor. Methods are provided to handle these common operations, plus an + * <code>open</code> command selects a default registered application for the + * specified file type. For example, opening an odf file results in launching + * OpenOffice. If an operation is not supported, or the application fails to + * launch, an exception is generated. + * + * <strong>Implementation note: </strong>As this class is used to manage Desktop + * integration, we provide some extension to configure the behaviour of this + * class depending on the type of dektop that is detected.<br /> + * + * First of all, we support 5 system properties that can be used to define + * the application to launch in any given case. These properties are:<br /> + * <br /> + * <code>gnu.java.awt.peer.Desktop.html.command</code><br /> + * <code>gnu.java.awt.peer.Desktop.mail.command</code><br /> + * <code>gnu.java.awt.peer.Desktop.edit.command</code><br /> + * <code>gnu.java.awt.peer.Desktop.print.command</code><br /> + * <code>gnu.java.awt.peer.Desktop.open.command</code><br /> + * <br /> + * <br /> + * These can be specified from the command line and have priority on every + * other setting.<br /> + * <br /> + * The second method supported is defining a Java preference.<br /> + * The main preference node is a <strong>user node</strong> relative to the + * class <code>gnu.java.awt.peer.ClasspathDesktopPeer</code>. This node + * contains a child for each supported operation. The key for each type is + * always <code>command</code>: + * <br /><br /> + * <code>gnu.java.awt.peer.Desktop.html.command</code><br /> + * <code>gnu.java.awt.peer.Desktop.mail.command</code><br /> + * <code>gnu.java.awt.peer.Desktop.edit.command</code><br /> + * <code>gnu.java.awt.peer.Desktop.print.command</code><br /> + * <code>gnu.java.awt.peer.Desktop.open.command</code><br /> + * <br /> + * <br /> + * The access to these keys is done with the Preference API or, if outside + * of the java scope, is done in a backend dependent way. For example, + * with the GConf backend, you can access these properties + * with (may not be accurate on your system): + * <br /><br /> + * <code> + * gconftool-2 -g /apps/classpath/gnu/java/awt/peer/Desktop/html/command + * </code> + * + * @author Mario Torre <neugens@limasoftware.net> + * @since 1.6 + */ +public class Desktop +{ + /** + * Represents an action type supported by a platform. + * + * To determine if a given action is supported by the platform, + * use the <code>Desktop.isSupported(java.awt.Desktop.Action)</code> + * method. + * + * @author Mario Torre <neugens@limasoftware.net> + */ + public enum Action + { + BROWSE, EDIT, MAIL, OPEN, PRINT + } + + private DesktopPeer peer; + + private Desktop() + { + /* nothing to be done */ + } + + /** + * Returns an istance of the Desktop Class. + * + * If this implementation does not support Desktop, an + * UnsupportedOperationException will be thrown. + * Also, an HeadlessException will be generated if + * GraphicsEnvironment.isHeadless() returns true. + * + * @throws UnsupportedOperationException + * @throws HeadlessException + */ + public static Desktop getDesktop() throws UnsupportedOperationException, + HeadlessException + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + + if (!Desktop.isDesktopSupported()) + throw new UnsupportedOperationException(); + + Desktop desktop = new Desktop(); + desktop.peer = Toolkit.getDefaultToolkit().createDesktopPeer(desktop); + + return desktop; + } + + /** + * Check if this implementation supports Desktop. + * If true, use getDesktop to get an instance of this class. + * + * This implementation will return false when GraphicsEnvironment.isHeadless + * returns true. + * + * @return true if this class is supported on the current platform; + * false otherwise + */ + private static boolean isDesktopSupported() + { + if (GraphicsEnvironment.isHeadless()) + return false; + + return true; + } + + /** + * Check if the given Action is supported by this implementation. + * + * @param action + * @return + */ + public boolean isSupported(Desktop.Action action) + { + return peer.isSupported(action); + } + + /** + * Launches the Desktop default browser to open the given <code>uri</code>. + * + * If a security manager exists and denies + * AWTPermission("showWindowWithoutWarningBanner"),a SecurityException will + * be generated. + * + * @param uri + * @throws IOException + */ + public void browse(URI uri) + throws IOException + { + peer.browse(uri); + } + + /** + * Launch the edit command to edit this file. + * File should already exist before the editing starts. + * + * If a security manager exists and + * SecurityManager.checkRead(java.lang.String) method denies read access to + * the file, or SecurityManager.checkPrintJobAccess() method denies the + * permission to print the file, a SecurityException will be generated. + * + * @param file + * @throws IOException + */ + public void edit(File file) + throws IOException + { + peer.edit(file); + } + + /** + * Launches the Desktop default mailer. + * + * If a security manager exists and denies + * AWTPermission("showWindowWithoutWarningBanner"), a SecurityException will + * be generated. + * + * @throws IOException + */ + public void mail() + throws IOException + { + peer.mail(); + } + + /** + * Launches the Desktop default mailer, with the given mailtoURI + * as agrument. The <code>mailtoURI</code> must conform to the + * {@link http://www.ietf.org/rfc/rfc2368.txt The mailto URL scheme (RFC 2368)} + * + * If a security manager exists and denies + * AWTPermission("showWindowWithoutWarningBanner"), a SecurityException will + * be generated. + * + * @param mailtoURI + * @throws IOException + */ + public void mail(URI mailtoURI) + throws IOException + { + peer.mail(mailtoURI); + } + + /** + * Launches the Desktop default application to open the given File. + * If <code>file</code> is a directory, a file manager is launched. + * + * @param file + * @throws IOException + */ + public void open(File file) + throws IOException + { + peer.open(file); + } + + /** + * Launch the print program to print this file. + * + * @param file + * @throws IOException + */ + public void print(File file) + throws IOException + { + peer.print(file); + } +} diff --git a/libjava/classpath/java/awt/EventDispatchThread.java b/libjava/classpath/java/awt/EventDispatchThread.java index 074a84975ac..9f81a794415 100644 --- a/libjava/classpath/java/awt/EventDispatchThread.java +++ b/libjava/classpath/java/awt/EventDispatchThread.java @@ -73,6 +73,9 @@ class EventDispatchThread extends Thread // Ignore and use default. } setPriority(priority); + + // Make sure that an event dispatch thread is never a daemon thread. + setDaemon(false); } public void run() diff --git a/libjava/classpath/java/awt/EventQueue.java b/libjava/classpath/java/awt/EventQueue.java index 74dbd5fb67d..eb17449a00c 100644 --- a/libjava/classpath/java/awt/EventQueue.java +++ b/libjava/classpath/java/awt/EventQueue.java @@ -128,10 +128,8 @@ public class EventQueue if (peekEvent() != null) return false; - Frame[] frames = Frame.getFrames(); - for (int i = 0; i < frames.length; ++i) - if (frames[i].isDisplayable()) - return false; + if (Frame.hasDisplayableFrames()) + return false; return true; } diff --git a/libjava/classpath/java/awt/Frame.java b/libjava/classpath/java/awt/Frame.java index d5cc7f53197..3cc8738c63f 100644 --- a/libjava/classpath/java/awt/Frame.java +++ b/libjava/classpath/java/awt/Frame.java @@ -40,9 +40,10 @@ exception statement from your version. */ package java.awt; import java.awt.peer.FramePeer; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Iterator; import java.util.Vector; import javax.accessibility.AccessibleContext; @@ -484,44 +485,72 @@ public class Frame extends Window implements MenuContainer return super.paramString () + ",title=" + title + resizable + state; } - private static ArrayList weakFrames = new ArrayList(); + /** + * The list of active frames. GC'ed frames get removed in noteFrame(). + */ + private static ArrayList<WeakReference<Frame>> weakFrames = + new ArrayList<WeakReference<Frame>>(); + + /** + * The death queue for all frames. + */ + private static ReferenceQueue weakFramesQueue = + new ReferenceQueue<Frame>(); private static void noteFrame(Frame f) { synchronized (weakFrames) { - weakFrames.add(new WeakReference(f)); + // Remove GCed frames from the list. + Reference ref = weakFramesQueue.poll(); + while (ref != null) + { + weakFrames.remove(ref); + ref = weakFramesQueue.poll(); + } + // Add new frame. + weakFrames.add(new WeakReference<Frame>(f)); } } + /** + * Returns <code>true</code> when there are any displayable frames, + * <code>false</code> otherwise. + * + * @return <code>true</code> when there are any displayable frames, + * <code>false</code> otherwise + */ + static boolean hasDisplayableFrames() + { + synchronized (weakFrames) + { + for (WeakReference<Frame> r : Frame.weakFrames) + { + Frame f = (Frame) r.get(); + if (f != null && f.isDisplayable()) + return true; + } + } + return false; + } + public static Frame[] getFrames() { - int n = 0; synchronized (weakFrames) - { - Iterator i = weakFrames.iterator(); - while (i.hasNext()) - { - WeakReference wr = (WeakReference) i.next(); - if (wr.get() != null) - ++n; - } - if (n == 0) - return new Frame[0]; - else - { - Frame[] frames = new Frame[n]; - n = 0; - i = weakFrames.iterator(); - while (i.hasNext()) - { - WeakReference wr = (WeakReference) i.next(); - if (wr.get() != null) - frames[n++] = (Frame) wr.get(); - } - return frames; - } - } + { + ArrayList<Frame> existingFrames = new ArrayList<Frame>(); + for (WeakReference<Frame> ref : weakFrames) + { + Frame f = ref.get(); + if (f != null) + { + existingFrames.add(f); + } + } + Frame[] frames = new Frame[existingFrames.size()]; + frames = existingFrames.toArray(frames); + return frames; + } } public void setState(int state) diff --git a/libjava/classpath/java/awt/GraphicsConfiguration.java b/libjava/classpath/java/awt/GraphicsConfiguration.java index 792b2cc1b2b..3cf8b65db8f 100644 --- a/libjava/classpath/java/awt/GraphicsConfiguration.java +++ b/libjava/classpath/java/awt/GraphicsConfiguration.java @@ -38,8 +38,6 @@ exception statement from your version. */ package java.awt; -import gnu.classpath.NotImplementedException; - import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; @@ -131,7 +129,8 @@ public abstract class GraphicsConfiguration ImageCapabilities caps) throws AWTException { - throw new AWTException("not implemented"); + // Must be overridden by implementations to check caps. + return createCompatibleVolatileImage(w, h); } /** @@ -150,6 +149,32 @@ public abstract class GraphicsConfiguration int transparency); /** + * Creates a volatile image with the specified capabilities and transparency. + * If the backend cannot meet the requested capabilities and transparency, + * an AWTException is thrown. + * + * @param width the width of the image + * @param height the height of the image + * @param caps the requested capabilities + * @param transparency the required transparency + * + * @return a volatile image with the specified capabilites and transparency + * + * @throws AWTException if the required capabilities and transparency cannot + * be met + * + * @since 1.5 + */ + public VolatileImage createCompatibleVolatileImage(int width, int height, + ImageCapabilities caps, + int transparency) + throws AWTException + { + // Must be overridden by implementations to check caps. + return createCompatibleVolatileImage(width, height, transparency); + } + + /** * Returns a buffered image optimized to this device, and with the specified * transparency, so that blitting can be supported in the buffered image. * diff --git a/libjava/classpath/java/awt/RenderingHints.java b/libjava/classpath/java/awt/RenderingHints.java index ce327e36947..e98a00c5bab 100644 --- a/libjava/classpath/java/awt/RenderingHints.java +++ b/libjava/classpath/java/awt/RenderingHints.java @@ -158,7 +158,7 @@ public class RenderingHints } } // class KeyImpl - private HashMap hintMap = new HashMap(); + private HashMap<Object,Object> hintMap = new HashMap<Object,Object>(); /** * A key for the 'antialiasing' hint. Permitted values are: @@ -711,9 +711,9 @@ public class RenderingHints Iterator iterator = m.keySet().iterator(); while (iterator.hasNext()) { - Key key = (Key) iterator.next(); - if (!key.isCompatibleValue(m.get(key))) - throw new IllegalArgumentException(); + Key key = (Key) iterator.next(); + if (!key.isCompatibleValue(m.get(key))) + throw new IllegalArgumentException(); } // map is OK, update hintMap.putAll(m); @@ -783,7 +783,7 @@ public class RenderingHints try { RenderingHints copy = (RenderingHints) super.clone(); - copy.hintMap = (HashMap) hintMap.clone(); + copy.hintMap = new HashMap<Object,Object>(hintMap); return copy; } catch (CloneNotSupportedException e) diff --git a/libjava/classpath/java/awt/Toolkit.java b/libjava/classpath/java/awt/Toolkit.java index 69040722e72..305402e9541 100644 --- a/libjava/classpath/java/awt/Toolkit.java +++ b/libjava/classpath/java/awt/Toolkit.java @@ -1,5 +1,5 @@ /* Toolkit.java -- AWT Toolkit superclass - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,6 +40,7 @@ exception statement from your version. */ package java.awt; import gnu.classpath.SystemProperties; +import gnu.java.awt.AWTUtilities; import gnu.java.awt.peer.GLightweightPeer; import gnu.java.awt.peer.headless.HeadlessToolkit; @@ -62,6 +63,7 @@ import java.awt.peer.CanvasPeer; import java.awt.peer.CheckboxMenuItemPeer; import java.awt.peer.CheckboxPeer; import java.awt.peer.ChoicePeer; +import java.awt.peer.DesktopPeer; import java.awt.peer.DialogPeer; import java.awt.peer.FileDialogPeer; import java.awt.peer.FontPeer; @@ -148,6 +150,15 @@ public abstract class Toolkit } /** + * + * @param target + * @return + * @throws HeadlessException + */ + protected abstract DesktopPeer createDesktopPeer(Desktop target) + throws HeadlessException; + + /** * Creates a peer object for the specified <code>Button</code>. * * @param target The <code>Button</code> to create the peer for. @@ -802,12 +813,11 @@ public abstract class Toolkit */ public boolean getLockingKeyState(int keyCode) { - if (keyCode != KeyEvent.VK_CAPS_LOCK - && keyCode != KeyEvent.VK_NUM_LOCK - && keyCode != KeyEvent.VK_SCROLL_LOCK) - throw new IllegalArgumentException(); - - throw new UnsupportedOperationException(); + if (AWTUtilities.isValidKey(keyCode)) + throw new UnsupportedOperationException + ("cannot get locking state of key code " + keyCode); + + throw new IllegalArgumentException("invalid key code " + keyCode); } /** diff --git a/libjava/classpath/java/awt/Window.java b/libjava/classpath/java/awt/Window.java index 41dff5577e0..2a59375ced9 100644 --- a/libjava/classpath/java/awt/Window.java +++ b/libjava/classpath/java/awt/Window.java @@ -87,7 +87,6 @@ public class Window extends Container implements Accessible private transient WindowListener windowListener; private transient WindowFocusListener windowFocusListener; private transient WindowStateListener windowStateListener; - private transient GraphicsConfiguration graphicsConfiguration; private transient boolean shown; @@ -132,13 +131,13 @@ public class Window extends Container implements Accessible setLayout(new BorderLayout()); GraphicsEnvironment g = GraphicsEnvironment.getLocalGraphicsEnvironment(); - graphicsConfiguration = g.getDefaultScreenDevice().getDefaultConfiguration(); + graphicsConfig = g.getDefaultScreenDevice().getDefaultConfiguration(); } Window(GraphicsConfiguration gc) { this(); - graphicsConfiguration = gc; + graphicsConfig = gc; } /** @@ -204,19 +203,11 @@ public class Window extends Container implements Accessible throw new IllegalArgumentException ("gc must be from a screen device"); if (gc == null) - graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment() - .getDefaultScreenDevice() - .getDefaultConfiguration(); + graphicsConfig = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice() + .getDefaultConfiguration(); else - graphicsConfiguration = gc; - } - - GraphicsConfiguration getGraphicsConfigurationImpl() - { - if (graphicsConfiguration != null) - return graphicsConfiguration; - - return super.getGraphicsConfigurationImpl(); + graphicsConfig = gc; } /** @@ -1073,9 +1064,14 @@ public class Window extends Container implements Accessible */ public GraphicsConfiguration getGraphicsConfiguration() { - if (graphicsConfiguration != null) return graphicsConfiguration; - if (peer != null) return peer.getGraphicsConfiguration(); - return null; + GraphicsConfiguration conf = graphicsConfig; + if (conf == null) + { + conf = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + graphicsConfig = conf; + } + return conf; } protected void processWindowFocusEvent(WindowEvent event) diff --git a/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java b/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java index e163fe067e2..7f296b5941a 100644 --- a/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java +++ b/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java @@ -38,12 +38,21 @@ exception statement from your version. */ package java.awt.datatransfer; -import gnu.classpath.NotImplementedException; - +import java.awt.Toolkit; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.WeakHashMap; /** @@ -72,19 +81,102 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable * This map maps native <code>String</code>s to lists of * <code>DataFlavor</code>s */ - private HashMap nativeToFlavorMap = new HashMap(); + private HashMap<String,List<DataFlavor>> nativeToFlavorMap = + new HashMap<String,List<DataFlavor>>(); /** * This map maps <code>DataFlavor</code>s to lists of native * <code>String</code>s */ - private HashMap flavorToNativeMap = new HashMap(); + private HashMap<DataFlavor, List<String>> flavorToNativeMap = + new HashMap<DataFlavor, List<String>>(); /** * Private constructor. */ private SystemFlavorMap () { + AccessController.doPrivileged + (new PrivilegedAction<Object>() + { + public Object run() + { + try + { + // Load installed flavormap.properties first. + String sep = File.separator; + File propsFile = + new File(System.getProperty("gnu.classpath.home.url") + + sep + "accessibility.properties"); + InputStream in = new FileInputStream(propsFile); + Properties props = new Properties(); + props.load(in); + in.close(); + + String augmented = Toolkit.getProperty("AWT.DnD.flavorMapFileURL", + null); + if (augmented != null) + { + URL url = new URL(augmented); + in = url.openStream(); + props.load(in); + } + setupMapping(props); + } + catch (IOException ex) + { + // Can't do anything about it. + } + return null; + } + }); + } + + /** + * Sets up the mapping from native to mime types and vice versa as specified + * in the flavormap.properties file. + * + * This is package private to avoid an accessor method. + * + * @param props the properties file + */ + void setupMapping(Properties props) + { + Enumeration propNames = props.propertyNames(); + while (propNames.hasMoreElements()) + { + try + { + String nat = (String) propNames.nextElement(); + String mime = (String) props.getProperty(nat); + // Check valid mime type. + MimeType type = new MimeType(mime); + DataFlavor flav = new DataFlavor(mime); + + List<DataFlavor> flavs = nativeToFlavorMap.get(nat); + if (flavs == null) + { + flavs = new ArrayList<DataFlavor>(); + nativeToFlavorMap.put(nat, flavs); + } + List<String> nats = flavorToNativeMap.get(flav); + if (nats == null) + { + nats = new ArrayList<String>(); + flavorToNativeMap.put(flav, nats); + } + flavs.add(flav); + nats.add(nat); + } + catch (ClassNotFoundException ex) + { + // Skip. + } + catch (MimeTypeParseException ex) + { + // Skip. + } + } } /** @@ -263,16 +355,52 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable * specified native and a DataFlavor whose MIME type is a decoded * version of the native. */ - public List<DataFlavor> getFlavorsForNative (String nat) - throws NotImplementedException + public List<DataFlavor> getFlavorsForNative(String nat) { - throw new Error ("Not implemented"); + List<DataFlavor> ret = new ArrayList<DataFlavor>(); + if (nat == null) + { + Collection<List<DataFlavor>> all = nativeToFlavorMap.values(); + for (List<DataFlavor> list : all) + { + for (DataFlavor flav : list) + { + if (! ret.contains(flav)) + ret.add(flav); + } + } + } + else + { + List<DataFlavor> list = nativeToFlavorMap.get(nat); + if (list != null) + ret.addAll(list); + } + return ret; } public List<String> getNativesForFlavor (DataFlavor flav) - throws NotImplementedException { - throw new Error ("Not implemented"); + List<String> ret = new ArrayList<String>(); + if (flav == null) + { + Collection<List<String>> all = flavorToNativeMap.values(); + for (List<String> list : all) + { + for (String nat : list) + { + if (! ret.contains(nat)) + ret.add(nat); + } + } + } + else + { + List<String> list = flavorToNativeMap.get(flav); + if (list != null) + ret.addAll(list); + } + return ret; } /** @@ -298,10 +426,10 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { if ((nativeStr == null) || (flavor == null)) throw new NullPointerException(); - List flavors = (List) nativeToFlavorMap.get(nativeStr); + List<DataFlavor> flavors = nativeToFlavorMap.get(nativeStr); if (flavors == null) { - flavors = new ArrayList(); + flavors = new ArrayList<DataFlavor>(); nativeToFlavorMap.put(nativeStr, flavors); } else @@ -336,10 +464,10 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { if ((nativeStr == null) || (flavor == null)) throw new NullPointerException(); - List natives = (List) flavorToNativeMap.get(flavor); + List<String> natives = flavorToNativeMap.get(flavor); if (natives == null) { - natives = new ArrayList(); + natives = new ArrayList<String>(); flavorToNativeMap.put(flavor, natives); } else diff --git a/libjava/classpath/java/awt/geom/GeneralPath.java b/libjava/classpath/java/awt/geom/GeneralPath.java index 1e9ede5ee67..fa27d1908c7 100644 --- a/libjava/classpath/java/awt/geom/GeneralPath.java +++ b/libjava/classpath/java/awt/geom/GeneralPath.java @@ -79,22 +79,17 @@ import java.awt.Shape; */ public final class GeneralPath implements Shape, Cloneable { - // WORKAROUND for gcj 4.0.x (x < 3) - // fully qualify PathIterator constants. - /** Same constant as {@link PathIterator#WIND_EVEN_ODD}. */ - public static final int WIND_EVEN_ODD - = java.awt.geom.PathIterator.WIND_EVEN_ODD; + public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; /** Same constant as {@link PathIterator#WIND_NON_ZERO}. */ - public static final int WIND_NON_ZERO - = java.awt.geom.PathIterator.WIND_NON_ZERO; + public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; /** Initial size if not specified. */ private static final int INIT_SIZE = 10; /** A big number, but not so big it can't survive a few float operations */ - private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; + private static final double BIG_VALUE = Double.MAX_VALUE / 10.0; /** The winding rule. * This is package-private to avoid an accessor method. diff --git a/libjava/classpath/java/awt/image/BufferedImage.java b/libjava/classpath/java/awt/image/BufferedImage.java index ef3141d0ead..c9879461ce2 100644 --- a/libjava/classpath/java/awt/image/BufferedImage.java +++ b/libjava/classpath/java/awt/image/BufferedImage.java @@ -39,7 +39,9 @@ exception statement from your version. */ package java.awt.image; import gnu.java.awt.Buffers; +import gnu.java.awt.ClasspathGraphicsEnvironment; import gnu.java.awt.ComponentDataBlitOp; +import gnu.java.awt.peer.gtk.CairoSurface; import java.awt.Graphics; import java.awt.Graphics2D; @@ -83,7 +85,7 @@ public class BufferedImage extends Image /** * Vector of TileObservers (or null) */ - Vector tileObservers; + Vector<TileObserver> tileObservers; /** * The image's WritableRaster @@ -143,39 +145,39 @@ public class BufferedImage extends Image { SampleModel sm = null; ColorModel cm = null; - boolean premultiplied = (type == BufferedImage.TYPE_INT_ARGB_PRE || - type == BufferedImage.TYPE_4BYTE_ABGR_PRE); + boolean premultiplied = (type == BufferedImage.TYPE_INT_ARGB_PRE + || type == BufferedImage.TYPE_4BYTE_ABGR_PRE); switch( type ) { case BufferedImage.TYPE_INT_RGB: - sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, - width, height, - new int[]{ 0x00FF0000, - 0x0000FF00, - 0x000000FF } ) ; - cm = new DirectColorModel( 24, 0xff0000, 0xff00, 0xff ); - break; + sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, + width, height, + new int[]{ 0x00FF0000, + 0x0000FF00, + 0x000000FF } ) ; + cm = new DirectColorModel( 24, 0xff0000, 0xff00, 0xff ); + break; case BufferedImage.TYPE_3BYTE_BGR: - sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, - width, height, - 3, width * 3, - new int[]{ 2, 1, 0 } ); - cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), - false, false, - BufferedImage.OPAQUE, - DataBuffer.TYPE_BYTE); + sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, + width, height, + 3, width * 3, + new int[]{ 2, 1, 0 } ); + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + false, false, + BufferedImage.OPAQUE, + DataBuffer.TYPE_BYTE); break; case BufferedImage.TYPE_INT_ARGB: case BufferedImage.TYPE_INT_ARGB_PRE: - sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, - width, height, - new int[]{ 0x00FF0000, - 0x0000FF00, - 0x000000FF, - 0xFF000000 } ); + sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, + width, height, + new int[]{ 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000 } ); if (premultiplied) cm = new DirectColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, 0xff0000, 0xff00, 0xff, 0xff000000, @@ -183,7 +185,8 @@ public class BufferedImage extends Image Buffers.smallestAppropriateTransferType(32)); else cm = new DirectColorModel( 32, 0xff0000, 0xff00, 0xff, 0xff000000 ); - break; + + break; case BufferedImage.TYPE_4BYTE_ABGR: case BufferedImage.TYPE_4BYTE_ABGR_PRE: @@ -195,57 +198,58 @@ public class BufferedImage extends Image true, premultiplied, BufferedImage.TRANSLUCENT, DataBuffer.TYPE_BYTE); - break; + break; case BufferedImage.TYPE_INT_BGR: - sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, - width, height, - new int[]{ 0x000000FF, - 0x0000FF00, - 0x00FF0000 } ) ; - cm = new DirectColorModel( 24, 0xff, 0xff00, 0xff0000 ); + sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, + width, height, + new int[]{ 0x000000FF, + 0x0000FF00, + 0x00FF0000 } ) ; + cm = new DirectColorModel( 24, 0xff, 0xff00, 0xff0000 ); break; case BufferedImage.TYPE_USHORT_565_RGB: - sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT, - width, height, - new int[]{ 0xF800, - 0x7E0, - 0x1F } ) ; - cm = new DirectColorModel( 16, 0xF800, 0x7E0, 0x1F ); - break; + sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT, + width, height, + new int[]{ 0xF800, + 0x7E0, + 0x1F } ) ; + cm = new DirectColorModel( 16, 0xF800, 0x7E0, 0x1F ); + break; + case BufferedImage.TYPE_USHORT_555_RGB: - sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT, - width, height, - new int[]{ 0x7C00, - 0x3E0, - 0x1F } ) ; - cm = new DirectColorModel( 15, 0x7C00, 0x3E0, 0x1F ); - break; + sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT, + width, height, + new int[]{ 0x7C00, + 0x3E0, + 0x1F } ) ; + cm = new DirectColorModel( 15, 0x7C00, 0x3E0, 0x1F ); + break; case BufferedImage.TYPE_BYTE_INDEXED: - cm = createDefaultIndexedColorModel( false ); + cm = createDefaultIndexedColorModel( false ); case BufferedImage.TYPE_BYTE_GRAY: - sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, - width, height, - 1, width, new int[]{ 0 } ); - break; + sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, + width, height, + 1, width, new int[]{ 0 } ); + break; case BufferedImage.TYPE_USHORT_GRAY: - sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_USHORT, - width, height, - 1, width, new int[]{ 0 } ); - break; + sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_USHORT, + width, height, + 1, width, new int[]{ 0 } ); + break; case BufferedImage.TYPE_BYTE_BINARY: - cm = createDefaultIndexedColorModel( true ); - sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, - width, height, 1); - break; + cm = createDefaultIndexedColorModel( true ); + sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, + width, height, 1); + break; default: - sm = null; + sm = null; } if( sm == null ) @@ -253,33 +257,41 @@ public class BufferedImage extends Image if( cm == null ) // only for the grayscale types { - int buftype; - int[] bits = new int[1]; - if( type == BufferedImage.TYPE_BYTE_GRAY ) - { - buftype = DataBuffer.TYPE_BYTE; - bits[0] = 8; - } - else - { - buftype = DataBuffer.TYPE_USHORT; - bits[0] = 16; - } - ColorSpace graySpace = ColorSpace.getInstance( ColorSpace.CS_GRAY ); - - cm = new ComponentColorModel( graySpace, bits, false, false, - Transparency.OPAQUE, buftype ); + int buftype; + int[] bits = new int[1]; + if( type == BufferedImage.TYPE_BYTE_GRAY ) + { + buftype = DataBuffer.TYPE_BYTE; + bits[0] = 8; + } + else + { + buftype = DataBuffer.TYPE_USHORT; + bits[0] = 16; + } + ColorSpace graySpace = ColorSpace.getInstance( ColorSpace.CS_GRAY ); + + cm = new ComponentColorModel( graySpace, bits, false, false, + Transparency.OPAQUE, buftype ); } - init( cm, - Raster.createWritableRaster(sm, new Point( 0, 0 ) ), - premultiplied, - null, // no properties - type ); + WritableRaster rst = null; + + // Attempt to create an accelerated backend for this image + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + if (env instanceof ClasspathGraphicsEnvironment) + rst = ((ClasspathGraphicsEnvironment)env).createRaster(cm, sm); + + // Default to a standard Java raster & databuffer if needed + if (rst == null) + rst = Raster.createWritableRaster(sm, new Point( 0, 0 ) ); + + init(cm, rst, premultiplied, + null, // no properties + type ); } - public BufferedImage(int w, int h, int type, - IndexColorModel indexcolormodel) + public BufferedImage(int w, int h, int type, IndexColorModel indexcolormodel) { if ((type != TYPE_BYTE_BINARY) && (type != TYPE_BYTE_INDEXED)) throw new IllegalArgumentException("Type must be TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED"); @@ -289,27 +301,21 @@ public class BufferedImage extends Image throw new IllegalArgumentException("Byte type cannot have a larger than 256-color palette."); init( indexcolormodel, - indexcolormodel.createCompatibleWritableRaster(w, h), - indexcolormodel.isAlphaPremultiplied(), - null, // no properties - type ); + indexcolormodel.createCompatibleWritableRaster(w, h), + indexcolormodel.isAlphaPremultiplied(), + null, // no properties + type ); } - public BufferedImage(ColorModel colormodel, - WritableRaster writableraster, - boolean premultiplied, - Hashtable<?,?> properties) + public BufferedImage(ColorModel colormodel, WritableRaster writableraster, + boolean premultiplied, Hashtable<?,?> properties) { - init(colormodel, writableraster, premultiplied, properties, - TYPE_CUSTOM); + init(colormodel, writableraster, premultiplied, properties, TYPE_CUSTOM); } - private void init(ColorModel cm, - WritableRaster writableraster, - boolean premultiplied, - Hashtable properties, - int type) + private void init(ColorModel cm, WritableRaster writableraster, + boolean premultiplied, Hashtable properties, int type) { raster = writableraster; colorModel = cm; @@ -329,29 +335,32 @@ public class BufferedImage extends Image { if( binary ) { - byte[] t = new byte[]{ 0, (byte)255 }; - return new IndexColorModel( 1, 2, t, t, t ); + byte[] t = new byte[]{ 0, (byte)255 }; + return new IndexColorModel( 1, 2, t, t, t ); } byte[] r = new byte[256]; byte[] g = new byte[256]; byte[] b = new byte[256]; + int index = 0; for( int i = 0; i < 6; i++ ) for( int j = 0; j < 6; j++ ) - for( int k = 0; k < 6; k++ ) - { - r[ index ] = (byte)(i * 51); - g[ index ] = (byte)(j * 51); - b[ index ] = (byte)(k * 51); - index++; - } + for( int k = 0; k < 6; k++ ) + { + r[ index ] = (byte)(i * 51); + g[ index ] = (byte)(j * 51); + b[ index ] = (byte)(k * 51); + index++; + } + while( index < 256 ) { - r[ index ] = g[ index ] = b[ index ] = - (byte)(18 + (index - 216) * 6); - index++; + r[ index ] = g[ index ] = b[ index ] = + (byte)(18 + (index - 216) * 6); + index++; } + return new IndexColorModel( 8, 256, r, g, b ); } @@ -375,12 +384,13 @@ public class BufferedImage extends Image // create a src child that has the right bounds... WritableRaster src = raster.createWritableChild(x, y, w, h, x, y, - null // same bands - ); + null); // same bands + if (src.getSampleModel () instanceof ComponentSampleModel && dest.getSampleModel () instanceof ComponentSampleModel) // Refer to ComponentDataBlitOp for optimized data blitting: ComponentDataBlitOp.INSTANCE.filter(src, dest); + else { // slower path @@ -397,7 +407,8 @@ public class BufferedImage extends Image return env.createGraphics (this); } - public void flush() { + public void flush() + { } public WritableRaster getAlphaRaster() @@ -512,26 +523,24 @@ public class BufferedImage extends Image public int getRGB(int x, int y) { - Object rgbElem = raster.getDataElements(x, y, - null // create as needed - ); + Object rgbElem = raster.getDataElements(x, y, null); return colorModel.getRGB(rgbElem); } - public int[] getRGB(int startX, int startY, int w, int h, - int[] rgbArray, - int offset, int scanlineStride) + public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, + int offset, int scanlineStride) { if (rgbArray == null) - { - /* - 000000000000000000 - 00000[#######----- [ = start - -----########----- ] = end - -----#######]00000 - 000000000000000000 */ - int size = (h-1)*scanlineStride + w; - rgbArray = new int[size]; + { + /* + 000000000000000000 + 00000[#######----- [ = start + -----########----- ] = end + -----#######]00000 + 000000000000000000 + */ + int size = (h-1)*scanlineStride + w; + rgbArray = new int[size]; } int endX = startX + w; @@ -547,15 +556,15 @@ public class BufferedImage extends Image Object rgbElem = null; for (int y=startY; y<endY; y++) { - int xoffset = offset; - for (int x=startX; x<endX; x++) - { - int rgb; - rgbElem = raster.getDataElements(x, y, rgbElem); - rgb = colorModel.getRGB(rgbElem); - rgbArray[xoffset++] = rgb; - } - offset += scanlineStride; + int xoffset = offset; + for (int x=startX; x<endX; x++) + { + int rgb; + rgbElem = raster.getDataElements(x, y, rgbElem); + rgb = colorModel.getRGB(rgbElem); + rgbArray[xoffset++] = rgb; + } + offset += scanlineStride; } return rgbArray; } @@ -572,14 +581,14 @@ public class BufferedImage extends Image public ImageProducer getSource() { - return new ImageProducer() { - - Vector consumers = new Vector(); + return new ImageProducer() + { + Vector<ImageConsumer> consumers = new Vector<ImageConsumer>(); public void addConsumer(ImageConsumer ic) { - if(!consumers.contains(ic)) - consumers.add(ic); + if(!consumers.contains(ic)) + consumers.add(ic); } public boolean isConsumer(ImageConsumer ic) @@ -589,7 +598,7 @@ public class BufferedImage extends Image public void removeConsumer(ImageConsumer ic) { - consumers.remove(ic); + consumers.remove(ic); } public void startProduction(ImageConsumer ic) @@ -610,9 +619,9 @@ public class BufferedImage extends Image consumers.add(ic); - for(int i=0;i<consumers.size();i++) + for(int i = 0; i < consumers.size(); i++) { - ImageConsumer c = (ImageConsumer) consumers.elementAt(i); + ImageConsumer c = consumers.elementAt(i); c.setHints(ImageConsumer.SINGLEPASS); c.setDimensions(getWidth(), getHeight()); c.setPixels(x, y, width, height, model, pixels, offset, stride); @@ -638,10 +647,8 @@ public class BufferedImage extends Image WritableRaster subRaster = getRaster().createWritableChild(x, y, w, h, 0, 0, null); - return new BufferedImage(getColorModel(), - subRaster, - isPremultiplied, - properties); + return new BufferedImage(getColorModel(), subRaster, isPremultiplied, + properties); } public Raster getTile(int tileX, int tileY) @@ -730,9 +737,7 @@ public class BufferedImage extends Image // create a dest child that has the right bounds... WritableRaster dest = - raster.createWritableChild(x, y, w, h, x, y, - null // same bands - ); + raster.createWritableChild(x, y, w, h, x, y, null); if (src.getSampleModel () instanceof ComponentSampleModel && dest.getSampleModel () instanceof ComponentSampleModel) @@ -762,14 +767,14 @@ public class BufferedImage extends Image Object rgbElem = null; for (int y=startY; y<endY; y++) { - int xoffset = offset; - for (int x=startX; x<endX; x++) - { - int argb = argbArray[xoffset++]; - rgbElem = colorModel.getDataElements(argb, rgbElem); - raster.setDataElements(x, y, rgbElem); - } - offset += scanlineStride; + int xoffset = offset; + for (int x=startX; x<endX; x++) + { + int argb = argbArray[xoffset++]; + rgbElem = colorModel.getDataElements(argb, rgbElem); + raster.setDataElements(x, y, rgbElem); + } + offset += scanlineStride; } } @@ -800,7 +805,7 @@ public class BufferedImage extends Image public void addTileObserver (TileObserver to) { if (tileObservers == null) - tileObservers = new Vector (); + tileObservers = new Vector<TileObserver>(); tileObservers.add (to); } diff --git a/libjava/classpath/java/awt/image/ComponentSampleModel.java b/libjava/classpath/java/awt/image/ComponentSampleModel.java index bccabbbcadb..73e7fb4d3d0 100644 --- a/libjava/classpath/java/awt/image/ComponentSampleModel.java +++ b/libjava/classpath/java/awt/image/ComponentSampleModel.java @@ -36,12 +36,8 @@ exception statement from your version. */ package java.awt.image; -import gnu.java.awt.Buffers; - import java.util.Arrays; -/* FIXME: This class does not yet support data type TYPE_SHORT */ - /** * ComponentSampleModel supports a flexible organization of pixel samples in * memory, permitting pixel samples to be interleaved by band, by scanline, @@ -88,9 +84,7 @@ public class ComponentSampleModel extends SampleModel * corresponding sample for the next pixel in the same row. */ protected int pixelStride; - - private boolean tightPixelPacking = false; - + /** * Creates a new sample model that assumes that all bands are stored in a * single bank of the {@link DataBuffer}. @@ -203,22 +197,6 @@ public class ComponentSampleModel extends SampleModel this.scanlineStride = scanlineStride; this.pixelStride = pixelStride; - // See if we can use some speedups - - /* FIXME: May these checks should be reserved for the - PixelInterleavedSampleModel? */ - - if (pixelStride == numBands) - { - tightPixelPacking = true; - for (int b = 0; b < numBands; b++) { - if ((bandOffsets[b] != b) || (bankIndices[b] != 0)) - { - tightPixelPacking = false; - break; - } - } - } } /** @@ -275,8 +253,30 @@ public class ComponentSampleModel extends SampleModel highestOffset = Math.max(highestOffset, bandOffsets[b]); int size = pixelStride * (width - 1) + scanlineStride * (height - 1) + highestOffset + 1; - - return Buffers.createBuffer(getDataType(), size, numBanks); + + DataBuffer buffer = null; + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + buffer = new DataBufferByte(size, numBanks); + break; + case DataBuffer.TYPE_SHORT: + buffer = new DataBufferShort(size, numBanks); + break; + case DataBuffer.TYPE_USHORT: + buffer = new DataBufferUShort(size, numBanks); + break; + case DataBuffer.TYPE_INT: + buffer = new DataBufferInt(size, numBanks); + break; + case DataBuffer.TYPE_FLOAT: + buffer = new DataBufferFloat(size, numBanks); + break; + case DataBuffer.TYPE_DOUBLE: + buffer = new DataBufferDouble(size, numBanks); + break; + } + return buffer; } /** @@ -424,239 +424,80 @@ public class ComponentSampleModel extends SampleModel */ public Object getDataElements(int x, int y, Object obj, DataBuffer data) { - int xyOffset = pixelStride * x + scanlineStride * y; - - int[] totalBandDataOffsets = new int[numBands]; - - /* Notice that band and bank offsets are different. Band offsets - are managed by the sample model, and bank offsets are managed - by the data buffer. Both must be accounted for. */ - - /* FIXME: For single pixels, it is probably easier to simple - call getElem instead of calculating the bank offset ourself. - - On the other hand, then we need to push the value through - the int type returned by the getElem method. */ - - int[] bankOffsets = data.getOffsets(); - - for (int b = 0; b < numBands; b++) + int type = getTransferType(); + int numDataEls = getNumDataElements(); + int offset = y * scanlineStride + x * pixelStride; + switch (type) { - totalBandDataOffsets[b] = bandOffsets[b] + bankOffsets[bankIndices[b]] - + xyOffset; - } - - try - { - switch (getTransferType()) + case DataBuffer.TYPE_BYTE: + byte[] bData; + if (obj == null) + bData = new byte[numDataEls]; + else + bData = (byte[]) obj; + for (int i = 0; i < numDataEls; i++) { - case DataBuffer.TYPE_BYTE: - DataBufferByte inByte = (DataBufferByte) data; - byte[] outByte = (byte[]) obj; - if (outByte == null) - outByte = new byte[numBands]; - - for (int b = 0; b < numBands; b++) - { - int dOffset = totalBandDataOffsets[b]; - outByte[b] = inByte.getData(bankIndices[b])[dOffset]; - } - return outByte; - - case DataBuffer.TYPE_USHORT: - DataBufferUShort inUShort = (DataBufferUShort) data; - short[] outUShort = (short[]) obj; - if (outUShort == null) - outUShort = new short[numBands]; - - for (int b = 0; b < numBands; b++) - { - int dOffset = totalBandDataOffsets[b]; - outUShort[b] = inUShort.getData(bankIndices[b])[dOffset]; - } - return outUShort; - - case DataBuffer.TYPE_SHORT: - DataBufferShort inShort = (DataBufferShort) data; - short[] outShort = (short[]) obj; - if (outShort == null) - outShort = new short[numBands]; - - for (int b = 0; b < numBands; b++) - { - int dOffset = totalBandDataOffsets[b]; - outShort[b] = inShort.getData(bankIndices[b])[dOffset]; - } - return outShort; - - case DataBuffer.TYPE_INT: - DataBufferInt inInt = (DataBufferInt) data; - int[] outInt = (int[]) obj; - if (outInt == null) - outInt = new int[numBands]; - - for (int b = 0; b < numBands; b++) - { - int dOffset = totalBandDataOffsets[b]; - outInt[b] = inInt.getData(bankIndices[b])[dOffset]; - } - return outInt; - - case DataBuffer.TYPE_FLOAT: - DataBufferFloat inFloat = (DataBufferFloat) data; - float[] outFloat = (float[]) obj; - if (outFloat == null) - outFloat = new float[numBands]; - - for (int b = 0; b < numBands; b++) - { - int dOffset = totalBandDataOffsets[b]; - outFloat[b] = inFloat.getData(bankIndices[b])[dOffset]; - } - return outFloat; - - case DataBuffer.TYPE_DOUBLE: - DataBufferDouble inDouble = (DataBufferDouble) data; - double[] outDouble = (double[]) obj; - if (outDouble == null) - outDouble = new double[numBands]; - - for (int b = 0; b < numBands; b++) - { - int dOffset = totalBandDataOffsets[b]; - outDouble[b] = inDouble.getData(bankIndices[b])[dOffset]; - } - return outDouble; - - default: - throw new IllegalStateException("unknown transfer type " - + getTransferType()); + bData[i] = (byte) data.getElem(bankIndices[i], + offset + bandOffsets[i]); } - } - catch (ArrayIndexOutOfBoundsException aioobe) - { - String msg = "While reading data elements, " + - "x=" + x + ", y=" + y +", " + ", xyOffset=" + xyOffset + - ", data.getSize()=" + data.getSize() + ": " + aioobe; - throw new ArrayIndexOutOfBoundsException(msg); - } - } - - /** - * Returns the samples for the pixels in the region defined by - * <code>(x, y, w, h)</code> in a primitive array (the array type is - * determined by the data type for this model). The <code>obj</code> - * argument provides an option to supply an existing array to hold the - * result, if this is <code>null</code> a new array will be allocated. - * - * @param x the x-coordinate. - * @param y the y-coordinate. - * @param w the width. - * @param h the height. - * @param obj a primitive array that, if not <code>null</code>, will be - * used to store and return the sample values. - * @param data the data buffer (<code>null</code> not permitted). - * - * @return An array of sample values for the specified pixels. - * - * @see #setDataElements(int, int, int, int, Object, DataBuffer) - */ - public Object getDataElements(int x, int y, int w, int h, Object obj, - DataBuffer data) - { - if (!tightPixelPacking) - { - return super.getDataElements(x, y, w, h, obj, data); - } - - // using get speedup - - // We can copy whole rows - int rowSize = w * numBands; - int dataSize = rowSize * h; - - DataBuffer transferBuffer = Buffers.createBuffer(getTransferType(), obj, - dataSize); - obj = Buffers.getData(transferBuffer); - - int inOffset = pixelStride * x + scanlineStride * y + data.getOffset(); - // Assumes only one band is used - - /* We don't add band offsets since we assume that bands have - offsets 0, 1, 2, ... */ - - // See if we can copy everything in one go - if (scanlineStride == rowSize) - { - // Collapse scan lines: - rowSize *= h; - // We ignore scanlineStride since it won't be of any use - h = 1; - } - - int outOffset = 0; - Object inArray = Buffers.getData(data); - for (int yd = 0; yd < h; yd++) - { - System.arraycopy(inArray, inOffset, obj, outOffset, rowSize); - inOffset += scanlineStride; - outOffset += rowSize; + obj = bData; + break; + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + short[] sData; + if (obj == null) + sData = new short[numDataEls]; + else + sData = (short[]) obj; + for (int i = 0; i < numDataEls; i++) + { + sData[i] = (short) data.getElem(bankIndices[i], + offset + bandOffsets[i]); + } + obj = sData; + break; + case DataBuffer.TYPE_INT: + int[] iData; + if (obj == null) + iData = new int[numDataEls]; + else + iData = (int[]) obj; + for (int i = 0; i < numDataEls; i++) + { + iData[i] = data.getElem(bankIndices[i], offset + bandOffsets[i]); + } + obj = iData; + break; + case DataBuffer.TYPE_FLOAT: + float[] fData; + if (obj == null) + fData = new float[numDataEls]; + else + fData = (float[]) obj; + for (int i = 0; i < numDataEls; i++) + { + fData[i] = data.getElemFloat(bankIndices[i], + offset + bandOffsets[i]); + } + obj = fData; + break; + case DataBuffer.TYPE_DOUBLE: + double[] dData; + if (obj == null) + dData = new double[numDataEls]; + else + dData = (double[]) obj; + for (int i = 0; i < numDataEls; i++) + { + dData[i] = data.getElemDouble(bankIndices[i], + offset + bandOffsets[i]); + } + obj = dData; + break; } return obj; } - /** - * Sets the samples for the pixels in the region defined by - * <code>(x, y, w, h)</code> from a supplied primitive array (the array type - * must be consistent with the data type for this model). - * - * @param x the x-coordinate. - * @param y the y-coordinate. - * @param w the width. - * @param h the height. - * @param obj a primitive array containing the sample values. - * @param data the data buffer (<code>null</code> not permitted). - * - * @see #getDataElements(int, int, int, int, Object, DataBuffer) - */ - public void setDataElements(int x, int y, int w, int h, - Object obj, DataBuffer data) - { - if (!tightPixelPacking) - { - super.setDataElements(x, y, w, h, obj, data); - return; - } - - // using set speedup, we can copy whole rows - int rowSize = w * numBands; - int dataSize = rowSize * h; - - DataBuffer transferBuffer - = Buffers.createBufferFromData(getTransferType(), obj, dataSize); - - int[] bankOffsets = data.getOffsets(); - - int outOffset = pixelStride * x + scanlineStride * y + bankOffsets[0]; - // same assumptions as in get... - - // See if we can copy everything in one go - if (scanlineStride == rowSize) - { - // Collapse scan lines: - rowSize *= h; - h = 1; - } - - int inOffset = 0; - Object outArray = Buffers.getData(data); - for (int yd = 0; yd < h; yd++) - { - System.arraycopy(obj, inOffset, outArray, outOffset, rowSize); - outOffset += scanlineStride; - inOffset += rowSize; - } - } /** * Returns all the samples for the pixel at location <code>(x, y)</code> @@ -764,78 +605,51 @@ public class ComponentSampleModel extends SampleModel */ public void setDataElements(int x, int y, Object obj, DataBuffer data) { - int offset = pixelStride * x + scanlineStride * y; - int[] totalBandDataOffsets = new int[numBands]; - int[] bankOffsets = data.getOffsets(); - for (int b = 0; b < numBands; b++) - totalBandDataOffsets[b] = bandOffsets[b] + bankOffsets[bankIndices[b]] - + offset; - - switch (getTransferType()) + int type = getTransferType(); + int numDataEls = getNumDataElements(); + int offset = y * scanlineStride + x * pixelStride; + switch (type) { case DataBuffer.TYPE_BYTE: - { - DataBufferByte out = (DataBufferByte) data; - byte[] in = (byte[]) obj; - - for (int b = 0; b < numBands; b++) - out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; - - return; - } - case DataBuffer.TYPE_USHORT: - { - DataBufferUShort out = (DataBufferUShort) data; - short[] in = (short[]) obj; - - for (int b = 0; b < numBands; b++) - out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; - - return; - } + byte[] bData = (byte[]) obj; + for (int i = 0; i < numDataEls; i++) + { + data.setElem(bankIndices[i], offset + bandOffsets[i], + ((int) bData[i]) & 0xFF); + } + break; case DataBuffer.TYPE_SHORT: - { - DataBufferShort out = (DataBufferShort) data; - short[] in = (short[]) obj; - - for (int b = 0; b < numBands; b++) - out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; - - return; - } + case DataBuffer.TYPE_USHORT: + short[] sData = (short[]) obj; + for (int i = 0; i < numDataEls; i++) + { + data.setElem(bankIndices[i], offset + bandOffsets[i], + ((int) sData[i]) & 0xFFFF); + } + break; case DataBuffer.TYPE_INT: - { - DataBufferInt out = (DataBufferInt) data; - int[] in = (int[]) obj; - - for (int b = 0; b < numBands; b++) - out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; - - return; - } + int[] iData = (int[]) obj; + for (int i = 0; i < numDataEls; i++) + { + data.setElem(bankIndices[i], offset + bandOffsets[i], iData[i]); + } + break; case DataBuffer.TYPE_FLOAT: - { - DataBufferFloat out = (DataBufferFloat) data; - float[] in = (float[]) obj; - - for (int b = 0; b < numBands; b++) - out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; - - return; - } + float[] fData = (float[]) obj; + for (int i = 0; i < numDataEls; i++) + { + data.setElemFloat(bankIndices[i], offset + bandOffsets[i], + fData[i]); + } + break; case DataBuffer.TYPE_DOUBLE: - { - DataBufferDouble out = (DataBufferDouble) data; - double[] in = (double[]) obj; - - for (int b = 0; b < numBands; b++) - out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; - - return; - } - default: - throw new UnsupportedOperationException("transfer type not " + - "implemented"); + double[] dData = (double[]) obj; + for (int i = 0; i < numDataEls; i++) + { + data.setElemDouble(bankIndices[i], offset + bandOffsets[i], + dData[i]); + } + break; } } diff --git a/libjava/classpath/java/awt/image/IndexColorModel.java b/libjava/classpath/java/awt/image/IndexColorModel.java index 46879cc98c9..d8a27d51ff1 100644 --- a/libjava/classpath/java/awt/image/IndexColorModel.java +++ b/libjava/classpath/java/awt/image/IndexColorModel.java @@ -134,7 +134,7 @@ public class IndexColorModel extends ColorModel if (size < 1) throw new IllegalArgumentException("size < 1"); map_size = size; - rgb = new int[size]; + rgb = createColorMap(bits, size); for (int i = 0; i < size; i++) { rgb[i] = (0xff000000 @@ -187,7 +187,7 @@ public class IndexColorModel extends ColorModel map_size = size; opaque = (alphas == null); - rgb = new int[size]; + rgb = createColorMap(bits, size); if (alphas == null) { for (int i = 0; i < size; i++) @@ -275,7 +275,7 @@ public class IndexColorModel extends ColorModel map_size = size; opaque = !hasAlpha; - rgb = new int[size]; + rgb = createColorMap(bits, size); if (hasAlpha) { int alpha; @@ -360,7 +360,7 @@ public class IndexColorModel extends ColorModel throw new IllegalArgumentException("size < 1"); map_size = size; opaque = !hasAlpha; - rgb = new int[size]; + rgb = createColorMap(bits, size); if (!hasAlpha) for (int i = 0; i < size; i++) rgb[i] = cmap[i + start] | 0xff000000; @@ -419,7 +419,7 @@ public class IndexColorModel extends ColorModel this.trans = -1; this.validBits = validBits; - rgb = new int[size]; + rgb = createColorMap(bits, size); if (!hasAlpha) for (int i = 0; i < size; i++) rgb[i] = cmap[i + start] | 0xff000000; @@ -726,4 +726,11 @@ public class IndexColorModel extends ColorModel } } } + + private int[] createColorMap(int bits, int size) + { + // According to a Mauve test, the RI allocates at least 256 entries here. + int realSize = Math.max(256, Math.max(1 << bits, size)); + return new int[realSize]; + } } diff --git a/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java b/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java index 9ed948c54f3..1b0ac3f7904 100644 --- a/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java +++ b/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java @@ -39,7 +39,6 @@ package java.awt.image; import java.util.Arrays; import gnu.java.awt.BitMaskExtent; -import gnu.java.awt.Buffers; /** * A <code>SampleModel</code> used when all samples are stored in a single @@ -151,14 +150,25 @@ public class SinglePixelPackedSampleModel extends SampleModel */ public DataBuffer createDataBuffer() { - int size; - // We can save (scanlineStride - width) pixels at the very end of // the buffer. The Sun reference implementation (J2SE 1.3.1 and // 1.4.1_01) seems to do this; tested with Mauve test code. - size = scanlineStride * (height - 1) + width; + int size = scanlineStride * (height - 1) + width; - return Buffers.createBuffer(getDataType(), size); + DataBuffer buffer = null; + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + buffer = new DataBufferByte(size); + break; + case DataBuffer.TYPE_USHORT: + buffer = new DataBufferUShort(size); + break; + case DataBuffer.TYPE_INT: + buffer = new DataBufferInt(size); + break; + } + return buffer; } /** @@ -253,73 +263,39 @@ public class SinglePixelPackedSampleModel extends SampleModel public Object getDataElements(int x, int y, Object obj, DataBuffer data) { - int offset = scanlineStride*y + x + data.getOffset(); - - return Buffers.getData(data, offset, obj, - 0, // destination offset, - 1 // length - ); - } - - /** - * This is a more efficient implementation of the default implementation in - * the super class. - * @param x The x-coordinate of the pixel rectangle to store in - * <code>obj</code>. - * @param y The y-coordinate of the pixel rectangle to store in - * <code>obj</code>. - * @param w The width of the pixel rectangle to store in <code>obj</code>. - * @param h The height of the pixel rectangle to store in <code>obj</code>. - * @param obj The primitive array to store the pixels into or null to force - * creation. - * @param data The DataBuffer that is the source of the pixel data. - * @return The primitive array containing the pixel data. - * @see java.awt.image.SampleModel#getDataElements(int, int, int, int, - * java.lang.Object, java.awt.image.DataBuffer) - */ - public Object getDataElements(int x, int y, int w, int h, Object obj, - DataBuffer data) - { - int size = w*h; - int dataSize = size; - Object pixelData = null; - switch (getTransferType()) - { + int type = getTransferType(); + Object ret = null; + switch (type) + { case DataBuffer.TYPE_BYTE: - pixelData = ((DataBufferByte) data).getData(); - if (obj == null) obj = new byte[dataSize]; + { + byte[] in = (byte[]) obj; + if (in == null) + in = new byte[1]; + in[0] = (byte) data.getElem(x + y * scanlineStride); + ret = in; + } break; - case DataBuffer.TYPE_USHORT: - pixelData = ((DataBufferUShort) data).getData(); - if (obj == null) obj = new short[dataSize]; - break; - case DataBuffer.TYPE_INT: - pixelData = ((DataBufferInt) data).getData(); - if (obj == null) obj = new int[dataSize]; - break; - default: - // Seems like the only sensible thing to do. - throw new ClassCastException(); - } - if(x == 0 && scanlineStride == w) - { - // The full width need to be copied therefore we can copy in one shot. - System.arraycopy(pixelData, scanlineStride*y + data.getOffset(), obj, - 0, size); - } - else - { - // Since we do not need the full width we need to copy line by line. - int outOffset = 0; - int dataOffset = scanlineStride*y + x + data.getOffset(); - for (int yy = y; yy<(y+h); yy++) + case DataBuffer.TYPE_USHORT: + { + short[] in = (short[]) obj; + if (in == null) + in = new short[1]; + in[0] = (short) data.getElem(x + y * scanlineStride); + ret = in; + } + break; + case DataBuffer.TYPE_INT: { - System.arraycopy(pixelData, dataOffset, obj, outOffset, w); - dataOffset += scanlineStride; - outOffset += w; + int[] in = (int[]) obj; + if (in == null) + in = new int[1]; + in[0] = data.getElem(x + y * scanlineStride); + ret = in; } + break; } - return obj; + return ret; } /** @@ -414,30 +390,29 @@ public class SinglePixelPackedSampleModel extends SampleModel public void setDataElements(int x, int y, Object obj, DataBuffer data) { - int transferType = getTransferType(); - switch (transferType) - { - case DataBuffer.TYPE_BYTE: - { - byte[] in = (byte[]) obj; - data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xff); - break; - } - case DataBuffer.TYPE_USHORT: - { - short[] in = (short[]) obj; + switch (transferType) + { + case DataBuffer.TYPE_BYTE: + { + byte[] in = (byte[]) obj; + data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xff); + } + break; + case DataBuffer.TYPE_USHORT: + { + short[] in = (short[]) obj; data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xffff); - break; - } - case DataBuffer.TYPE_INT: - { - int[] in = (int[]) obj; + } + break; + case DataBuffer.TYPE_INT: + { + int[] in = (int[]) obj; data.setElem(y * scanlineStride + x, in[0]); break; - } - } - } + } + } + } /** * Sets the samples for the pixel at (x, y) in the specified data buffer to @@ -479,7 +454,6 @@ public class SinglePixelPackedSampleModel extends SampleModel DataBuffer data) { int inOffset = 0; - int[] pixel = new int[numBands]; for (int yy=y; yy<(y+h); yy++) { int offset = scanlineStride*yy + x; diff --git a/libjava/classpath/java/awt/peer/DesktopPeer.java b/libjava/classpath/java/awt/peer/DesktopPeer.java new file mode 100644 index 00000000000..355d293b311 --- /dev/null +++ b/libjava/classpath/java/awt/peer/DesktopPeer.java @@ -0,0 +1,64 @@ +/* DesktopPeer.java -- Interface to enable access to common applications + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +package java.awt.peer; + +import java.awt.Desktop.Action; +import java.io.File; +import java.io.IOException; +import java.net.URI; + +/** + * @author Mario Torre <neugens@limasoftware.net> + * + */ +public interface DesktopPeer +{ + public void browse(URI url) throws IOException; + + public void edit(File file) throws IOException; + + public boolean isSupported(Action action); + + public void mail(URI mailtoURL) throws IOException; + + public void mail() throws IOException; + + public void open(File file) throws IOException; + + public void print(File file) throws IOException; +} diff --git a/libjava/classpath/java/awt/print/PrinterJob.java b/libjava/classpath/java/awt/print/PrinterJob.java index 8afada1675e..3715dd235ec 100644 --- a/libjava/classpath/java/awt/print/PrinterJob.java +++ b/libjava/classpath/java/awt/print/PrinterJob.java @@ -264,15 +264,12 @@ public abstract class PrinterJob * @return Array of stream print services, could be empty. * @since 1.4 */ - // FIXME: - // Enable when StreamPrintServiceFactory has lookupStreamServiceFactories -// public static StreamPrintServiceFactory[] lookupStreamPrintServices(String mimeType) -// { -// return StreamPrintServiceFactory.lookupStreamServiceFactories( -// new DocFlavor("application/x-java-jvm-local-objectref", -// "java.awt.print.Pageable"), -// mimeType); -// } + public static StreamPrintServiceFactory[] + lookupStreamPrintServices(String mimeType) + { + return StreamPrintServiceFactory.lookupStreamPrintServiceFactories( + DocFlavor.SERVICE_FORMATTED.PAGEABLE, mimeType); + } /** * Return the printer for this job. If print services aren't supported by diff --git a/libjava/classpath/java/beans/ConstructorProperties.java b/libjava/classpath/java/beans/ConstructorProperties.java new file mode 100644 index 00000000000..4c82c00337f --- /dev/null +++ b/libjava/classpath/java/beans/ConstructorProperties.java @@ -0,0 +1,72 @@ +/* ConstructorProperties.java - Associate constructor params with props + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.CONSTRUCTOR; + +/** + * An annotation used to associate the parameters of a + * constructor with the accessor methods that later provide + * access to these values. For example, the parameters of + * the constructor <code>Person(String name, int age)</code> + * may be linked to the bean's two accessors, <code>getName()</code> + * and <code>getAge()</code> using + * <code>@ConstructorProperties({"name","age"})</code>. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +@Documented @Retention(RUNTIME) @Target(CONSTRUCTOR) +public @interface ConstructorProperties +{ + + /** + * Contains the name of the accessor methods associated + * with each constructor parameter. + * + * @return the accessor method names corresponding to the + * constructor parameters. + */ + String[] value(); + +} diff --git a/libjava/classpath/java/beans/XMLEncoder.java b/libjava/classpath/java/beans/XMLEncoder.java index feff68bd360..7242d0c699e 100644 --- a/libjava/classpath/java/beans/XMLEncoder.java +++ b/libjava/classpath/java/beans/XMLEncoder.java @@ -248,7 +248,7 @@ public class XMLEncoder extends Encoder scanEngine.writeObject(o); - if (get(o) == null); + if (get(o) == null) super.writeObject(o); accessCounter--; diff --git a/libjava/classpath/java/io/File.java b/libjava/classpath/java/io/File.java index 5d1b3ec8516..f34b4dd2b5c 100644 --- a/libjava/classpath/java/io/File.java +++ b/libjava/classpath/java/io/File.java @@ -164,6 +164,29 @@ public class File implements Serializable, Comparable<File> } /** + * This method tests whether or not the current thread is allowed to + * to execute the file pointed to by this object. This will be true if and + * and only if 1) the file exists and 2) the <code>SecurityManager</code> + * (if any) allows access to the file via it's <code>checkExec</code> + * method 3) the file is executable. + * + * @return <code>true</code> if execution is allowed, + * <code>false</code> otherwise + * + * @exception SecurityException If the <code>SecurityManager</code> + * does not allow access to the file + */ + public boolean canExecute() + { + if (!VMFile.exists(path)) + return false; + + checkExec(); + + return VMFile.canExecute(path); + } + + /** * This method creates a new file of zero length with the same name as * the path of this <code>File</code> object if an only if that file * does not already exist. @@ -1123,6 +1146,153 @@ public class File implements Serializable, Comparable<File> } /** + * This method sets the owner's read permission for the File represented by + * this object. + * + * It is the same as calling <code>setReadable(readable, true)</code>. + * + * @param <code>readable</code> <code>true</code> to set read permission, + * <code>false</code> to unset the read permission. + * @return <code>true</code> if the file permissions are changed, + * <code>false</code> otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setReadable(boolean, boolean) + * @since 1.6 + */ + public boolean setReadable(boolean readable) + { + return setReadable(readable, true); + } + + /** + * This method sets the read permissions for the File represented by + * this object. + * + * If <code>ownerOnly</code> is set to <code>true</code> then only the + * read permission bit for the owner of the file is changed. + * + * If <code>ownerOnly</code> is set to <code>false</code>, the file + * permissions are changed so that the file can be read by everyone. + * + * On unix like systems this sets the <code>user</code>, <code>group</code> + * and <code>other</code> read bits and is equal to call + * <code>chmod a+r</code> on the file. + * + * @param <code>readable</code> <code>true</code> to set read permission, + * <code>false</code> to unset the read permission. + * @param <code>ownerOnly</code> <code>true</code> to set read permission + * for owner only, <code>false</code> for all. + * @return <code>true</code> if the file permissions are changed, + * <code>false</code> otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setReadable(boolean) + * @since 1.6 + */ + public boolean setReadable(boolean readable, boolean ownerOnly) + { + checkWrite(); + return VMFile.setReadable(path, readable, ownerOnly); + } + + /** + * This method sets the owner's write permission for the File represented by + * this object. + * + * It is the same as calling <code>setWritable(readable, true)</code>. + * + * @param <code>writable</code> <code>true</code> to set write permission, + * <code>false</code> to unset write permission. + * @return <code>true</code> if the file permissions are changed, + * <code>false</code> otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setWritable(boolean, boolean) + * @since 1.6 + */ + public boolean setWritable(boolean writable) + { + return setWritable(writable, true); + } + + /** + * This method sets the write permissions for the File represented by + * this object. + * + * If <code>ownerOnly</code> is set to <code>true</code> then only the + * write permission bit for the owner of the file is changed. + * + * If <code>ownerOnly</code> is set to <code>false</code>, the file + * permissions are changed so that the file can be written by everyone. + * + * On unix like systems this set the <code>user</code>, <code>group</code> + * and <code>other</code> write bits and is equal to call + * <code>chmod a+w</code> on the file. + * + * @param <code>writable</code> <code>true</code> to set write permission, + * <code>false</code> to unset write permission. + * @param <code>ownerOnly</code> <code>true</code> to set write permission + * for owner only, <code>false</code> for all. + * @return <code>true</code> if the file permissions are changed, + * <code>false</code> otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setWritable(boolean) + * @since 1.6 + */ + public boolean setWritable(boolean writable, boolean ownerOnly) + { + checkWrite(); + return VMFile.setWritable(path, writable, ownerOnly); + } + + /** + * This method sets the owner's execute permission for the File represented + * by this object. + * + * It is the same as calling <code>setExecutable(readable, true)</code>. + * + * @param <code>executable</code> <code>true</code> to set execute permission, + * <code>false</code> to unset execute permission. + * @return <code>true</code> if the file permissions are changed, + * <code>false</code> otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setExecutable(boolean, boolean) + * @since 1.6 + */ + public boolean setExecutable(boolean executable) + { + return setExecutable(executable, true); + } + + /** + * This method sets the execute permissions for the File represented by + * this object. + * + * If <code>ownerOnly</code> is set to <code>true</code> then only the + * execute permission bit for the owner of the file is changed. + * + * If <code>ownerOnly</code> is set to <code>false</code>, the file + * permissions are changed so that the file can be executed by everyone. + * + * On unix like systems this set the <code>user</code>, <code>group</code> + * and <code>other</code> write bits and is equal to call + * <code>chmod a+x</code> on the file. + * + * @param <code>executable</code> <code>true</code> to set write permission, + * <code>false</code> to unset write permission. + * @param <code>ownerOnly</code> <code>true</code> to set write permission + * for owner only, <code>false</code> for all. + * @return <code>true</code> if the file permissions are changed, + * <code>false</code> otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setExecutable(boolean) + * @since 1.6 + */ + public boolean setExecutable(boolean executable, boolean ownerOnly) + { + checkWrite(); + return VMFile.setExecutable(path, executable, ownerOnly); + } + + /** * This method sets the file represented by this object to be read only. * A read only file or directory cannot be modified. Please note that * GNU systems allow read only files to be deleted if the directory it @@ -1315,6 +1485,15 @@ public class File implements Serializable, Comparable<File> s.checkRead(path); } + private void checkExec() + { + // Check the SecurityManager + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkExec(path); + } + /** * Calling this method requests that the file represented by this object * be deleted when the virtual machine exits. Note that this request cannot diff --git a/libjava/classpath/java/io/ObjectInputStream.java b/libjava/classpath/java/io/ObjectInputStream.java index 735d46cd920..37b2b64489c 100644 --- a/libjava/classpath/java/io/ObjectInputStream.java +++ b/libjava/classpath/java/io/ObjectInputStream.java @@ -39,6 +39,7 @@ exception statement from your version. */ package java.io; +import gnu.classpath.Pair; import gnu.classpath.VMStackWalker; import java.lang.reflect.Array; @@ -50,10 +51,11 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; +import java.util.Map; import java.util.TreeSet; -import java.util.Vector; /** * @author Tom Tromey (tromey@redhat.com) @@ -104,7 +106,7 @@ public class ObjectInputStream extends InputStream this.blockDataInput = new DataInputStream(this); this.realInputStream = new DataInputStream(in); this.nextOID = baseWireHandle; - this.objectLookupTable = new Vector<Object>(); + handles = new HashMap<Integer,Pair<Boolean,Object>>(); this.classLookupTable = new Hashtable<Class,ObjectStreamClass>(); setBlockDataMode(true); readStreamHeader(); @@ -132,6 +134,70 @@ public class ObjectInputStream extends InputStream public final Object readObject() throws ClassNotFoundException, IOException { + return readObject(true); + } + + /** + * <p> + * Returns the next deserialized object read from the + * underlying stream in an unshared manner. Any object + * returned by this method will not be returned by + * subsequent calls to either this method or {@link #readObject()}. + * </p> + * <p> + * This behaviour is achieved by: + * </p> + * <ul> + * <li>Marking the handles created by successful calls to this + * method, so that future calls to {@link #readObject()} or + * {@link #readUnshared()} will throw an {@link ObjectStreamException} + * rather than returning the same object reference.</li> + * <li>Throwing an {@link ObjectStreamException} if the next + * element in the stream is a reference to an earlier object.</li> + * </ul> + * + * @return a reference to the deserialized object. + * @throws ClassNotFoundException if the class of the object being + * deserialized can not be found. + * @throws StreamCorruptedException if information in the stream + * is inconsistent. + * @throws ObjectStreamException if the next object has already been + * returned by an earlier call to this + * method or {@link #readObject()}. + * @throws OptionalDataException if primitive data occurs next in the stream. + * @throws IOException if an I/O error occurs from the stream. + * @since 1.4 + * @see #readObject() + */ + public Object readUnshared() + throws IOException, ClassNotFoundException + { + return readObject(false); + } + + /** + * Returns the next deserialized object read from the underlying stream. + * + * This method can be overriden by a class by implementing + * <code>private void readObject (ObjectInputStream)</code>. + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. This method can also throw Errors and + * RuntimeExceptions if caused by existing readResolve() user code. + * + * @param shared true if handles created by this call should be shared + * with later calls. + * @return The object read from the underlying stream. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception IOException Exception from underlying + * <code>InputStream</code>. + */ + private final Object readObject(boolean shared) + throws ClassNotFoundException, IOException + { if (this.useSubclassMethod) return readObjectOverride(); @@ -146,7 +212,7 @@ public class ObjectInputStream extends InputStream try { - ret_val = parseContent(marker); + ret_val = parseContent(marker, shared); } finally { @@ -163,13 +229,15 @@ public class ObjectInputStream extends InputStream * byte indicating its type. * * @param marker the byte marker. + * @param shared true if handles created by this call should be shared + * with later calls. * @return an object which represents the parsed content. * @throws ClassNotFoundException if the class of an object being * read in cannot be found. * @throws IOException if invalid data occurs or one is thrown by the * underlying <code>InputStream</code>. */ - private Object parseContent(byte marker) + private Object parseContent(byte marker, boolean shared) throws ClassNotFoundException, IOException { Object ret_val; @@ -207,6 +275,9 @@ public class ObjectInputStream extends InputStream int oid = realInputStream.readInt(); if(dump) dumpElementln(Integer.toHexString(oid)); ret_val = lookupHandle(oid); + if (!shared) + throw new + InvalidObjectException("References can not be read unshared."); break; } @@ -215,7 +286,7 @@ public class ObjectInputStream extends InputStream if(dump) dumpElementln("CLASS"); ObjectStreamClass osc = (ObjectStreamClass)readObject(); Class clazz = osc.forClass(); - assignNewHandle(clazz); + assignNewHandle(clazz,shared); ret_val = clazz; break; } @@ -229,7 +300,7 @@ public class ObjectInputStream extends InputStream // TC_PROXYCLASSDESC newHandle proxyClassDescInfo // i.e. we have to assign the handle immediately after // reading the marker. - int handle = assignNewHandle("Dummy proxy"); + int handle = assignNewHandle("Dummy proxy",shared); /* END GCJ LOCAL */ int n_intf = this.realInputStream.readInt(); @@ -260,7 +331,7 @@ public class ObjectInputStream extends InputStream } } /* GCJ LOCAL */ - rememberHandle(osc,handle); + rememberHandle(osc,shared,handle); /* END GCJ LOCAL */ if (!is_consumed) @@ -301,7 +372,8 @@ public class ObjectInputStream extends InputStream if(dump) dumpElement("STRING="); String s = this.realInputStream.readUTF(); if(dump) dumpElementln(s); - ret_val = processResolution(null, s, assignNewHandle(s)); + ret_val = processResolution(null, s, assignNewHandle(s,shared), + shared); break; } @@ -314,12 +386,12 @@ public class ObjectInputStream extends InputStream int length = this.realInputStream.readInt(); if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType); Object array = Array.newInstance(componentType, length); - int handle = assignNewHandle(array); + int handle = assignNewHandle(array,shared); readArrayElements(array, componentType); if(dump) for (int i = 0, len = Array.getLength(array); i < len; i++) dumpElementln(" ELEMENT[" + i + "]=", Array.get(array, i)); - ret_val = processResolution(null, array, handle); + ret_val = processResolution(null, array, handle, shared); break; } @@ -337,7 +409,7 @@ public class ObjectInputStream extends InputStream { Externalizable obj = osc.newInstance(); - int handle = assignNewHandle(obj); + int handle = assignNewHandle(obj,shared); boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0); @@ -355,14 +427,14 @@ public class ObjectInputStream extends InputStream throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method."); } - ret_val = processResolution(osc, obj, handle); + ret_val = processResolution(osc, obj, handle,shared); break; } // end if (osc.realClassIsExternalizable) Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor); - int handle = assignNewHandle(obj); + int handle = assignNewHandle(obj,shared); Object prevObject = this.currentObject; ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; TreeSet<ValidatorAndPriority> prevObjectValidators = @@ -404,7 +476,7 @@ public class ObjectInputStream extends InputStream byte writeMarker = this.realInputStream.readByte(); while (writeMarker != TC_ENDBLOCKDATA) { - parseContent(writeMarker); + parseContent(writeMarker, shared); writeMarker = this.realInputStream.readByte(); } if(dump) dumpElementln("yes"); @@ -419,7 +491,7 @@ public class ObjectInputStream extends InputStream this.currentObject = prevObject; this.currentObjectStreamClass = prevObjectStreamClass; - ret_val = processResolution(osc, obj, handle); + ret_val = processResolution(osc, obj, handle, shared); if (currentObjectValidators != null) invokeValidators(); this.currentObjectValidators = prevObjectValidators; @@ -453,7 +525,7 @@ public class ObjectInputStream extends InputStream dumpElementln("CONSTANT NAME = " + constantName); Class clazz = osc.forClass(); Enum instance = Enum.valueOf(clazz, constantName); - assignNewHandle(instance); + assignNewHandle(instance,shared); ret_val = instance; break; } @@ -554,7 +626,7 @@ public class ObjectInputStream extends InputStream ObjectStreamField[] fields = new ObjectStreamField[field_count]; ObjectStreamClass osc = new ObjectStreamClass(name, uid, flags, fields); - assignNewHandle(osc); + assignNewHandle(osc,true); for (int i = 0; i < field_count; i++) { @@ -1555,13 +1627,15 @@ public class ObjectInputStream extends InputStream * Assigns the next available handle to <code>obj</code>. * * @param obj The object for which we want a new handle. + * @param shared True if the handle should be shared + * with later calls. * @return A valid handle for the specified object. */ - private int assignNewHandle(Object obj) + private int assignNewHandle(Object obj, boolean shared) { int handle = this.nextOID; this.nextOID = handle + 1; - rememberHandle(obj,handle); + rememberHandle(obj,shared,handle); return handle; } @@ -1569,40 +1643,44 @@ public class ObjectInputStream extends InputStream * Remember the object associated with the given handle. * * @param obj an object - * + * @param shared true if the reference should be shared + * with later calls. * @param handle a handle, must be >= baseWireHandle * * @see #lookupHandle */ - private void rememberHandle(Object obj, int handle) + private void rememberHandle(Object obj, boolean shared, + int handle) { - Vector olt = this.objectLookupTable; - handle = handle - baseWireHandle; - - if (olt.size() <= handle) - olt.setSize(handle + 1); - - olt.set(handle, obj); + handles.put(handle, new Pair<Boolean,Object>(shared, obj)); } /** * Look up the object associated with a given handle. * * @param handle a handle, must be >= baseWireHandle - * * @return the object remembered for handle or null if none. - * + * @throws StreamCorruptedException if the handle is invalid. + * @throws InvalidObjectException if the reference is not shared. * @see #rememberHandle */ private Object lookupHandle(int handle) + throws ObjectStreamException { - Vector olt = this.objectLookupTable; - handle = handle - baseWireHandle; - Object result = handle < olt.size() ? olt.get(handle) : null; - return result; + Pair<Boolean,Object> result = handles.get(handle); + if (result == null) + throw new StreamCorruptedException("The handle, " + + Integer.toHexString(handle) + + ", is invalid."); + if (!result.getLeft()) + throw new InvalidObjectException("The handle, " + + Integer.toHexString(handle) + + ", is not shared."); + return result.getRight(); } - private Object processResolution(ObjectStreamClass osc, Object obj, int handle) + private Object processResolution(ObjectStreamClass osc, Object obj, int handle, + boolean shared) throws IOException { if (osc != null && obj instanceof Serializable) @@ -1633,13 +1711,34 @@ public class ObjectInputStream extends InputStream if (this.resolveEnabled) obj = resolveObject(obj); - rememberHandle(obj, handle); + rememberHandle(obj, shared, handle); + if (!shared) + { + if (obj instanceof byte[]) + return ((byte[]) obj).clone(); + if (obj instanceof short[]) + return ((short[]) obj).clone(); + if (obj instanceof int[]) + return ((int[]) obj).clone(); + if (obj instanceof long[]) + return ((long[]) obj).clone(); + if (obj instanceof char[]) + return ((char[]) obj).clone(); + if (obj instanceof boolean[]) + return ((boolean[]) obj).clone(); + if (obj instanceof float[]) + return ((float[]) obj).clone(); + if (obj instanceof double[]) + return ((double[]) obj).clone(); + if (obj instanceof Object[]) + return ((Object[]) obj).clone(); + } return obj; } private void clearHandles() { - this.objectLookupTable.clear(); + handles.clear(); this.nextOID = baseWireHandle; } @@ -1966,7 +2065,7 @@ public class ObjectInputStream extends InputStream private boolean useSubclassMethod; private int nextOID; private boolean resolveEnabled; - private Vector<Object> objectLookupTable; + private Map<Integer,Pair<Boolean,Object>> handles; private Object currentObject; private ObjectStreamClass currentObjectStreamClass; private TreeSet<ValidatorAndPriority> currentObjectValidators; diff --git a/libjava/classpath/java/io/ObjectOutputStream.java b/libjava/classpath/java/io/ObjectOutputStream.java index 316b9070133..b1894b36882 100644 --- a/libjava/classpath/java/io/ObjectOutputStream.java +++ b/libjava/classpath/java/io/ObjectOutputStream.java @@ -170,6 +170,7 @@ public class ObjectOutputStream extends OutputStream * If an exception is thrown from this method, the stream is left in * an undefined state. * + * @param obj the object to serialize. * @exception NotSerializableException An attempt was made to * serialize an <code>Object</code> that is not serializable. * @@ -178,9 +179,71 @@ public class ObjectOutputStream extends OutputStream * * @exception IOException Exception from underlying * <code>OutputStream</code>. + * @see #writeUnshared(Object) */ public final void writeObject(Object obj) throws IOException { + writeObject(obj, true); + } + + /** + * Writes an object to the stream in the same manner as + * {@link #writeObject(Object)}, but without the use of + * references. As a result, the object is always written + * to the stream in full. Likewise, if an object is written + * by this method and is then later written again by + * {@link #writeObject(Object)}, both calls will write out + * the object in full, as the later call to + * {@link #writeObject(Object)} will know nothing of the + * earlier use of {@link #writeUnshared(Object)}. + * + * @param obj the object to serialize. + * @throws NotSerializableException if the object being + * serialized does not implement + * {@link Serializable}. + * @throws InvalidClassException if a problem occurs with + * the class of the object being + * serialized. + * @throws IOException if an I/O error occurs on the underlying + * <code>OutputStream</code>. + * @since 1.4 + * @see #writeObject(Object) + */ + public void writeUnshared(Object obj) + throws IOException + { + writeObject(obj, false); + } + + /** + * Writes a representation of <code>obj</code> to the underlying + * output stream by writing out information about its class, then + * writing out each of the objects non-transient, non-static + * fields. If any of these fields are other objects, + * they are written out in the same manner. + * + * This method can be overriden by a class by implementing + * <code>private void writeObject (ObjectOutputStream)</code>. + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. + * + * @param obj the object to serialize. + * @param shared true if the serialized object should be + * shared with later calls. + * @exception NotSerializableException An attempt was made to + * serialize an <code>Object</code> that is not serializable. + * + * @exception InvalidClassException Somebody tried to serialize + * an object which is wrongly formatted. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + * @see #writeUnshared(Object) + */ + private final void writeObject(Object obj, boolean shared) + throws IOException + { if (useSubclassMethod) { if (dump) @@ -212,7 +275,7 @@ public class ObjectOutputStream extends OutputStream } int handle = findHandle(obj); - if (handle >= 0) + if (handle >= 0 && shared) { realOutput.writeByte(TC_REFERENCE); realOutput.writeInt(handle); @@ -243,7 +306,8 @@ public class ObjectOutputStream extends OutputStream writeObject(osc.getSuper()); } - assignNewHandle(obj); + if (shared) + assignNewHandle(obj); break; } @@ -263,7 +327,8 @@ public class ObjectOutputStream extends OutputStream /* TC_ENUM classDesc newHandle enumConstantName */ realOutput.writeByte(TC_ENUM); writeObject(osc); - assignNewHandle(obj); + if (shared) + assignNewHandle(obj); writeObject(((Enum) obj).name()); break; } @@ -299,7 +364,8 @@ public class ObjectOutputStream extends OutputStream if (obj instanceof String) { realOutput.writeByte(TC_STRING); - assignNewHandle(obj); + if (shared) + assignNewHandle(obj); realOutput.writeUTF((String)obj); break; } @@ -308,7 +374,8 @@ public class ObjectOutputStream extends OutputStream { realOutput.writeByte(TC_ARRAY); writeObject(osc); - assignNewHandle(obj); + if (shared) + assignNewHandle(obj); writeArraySizeAndElements(obj, clazz.getComponentType()); break; } @@ -316,11 +383,12 @@ public class ObjectOutputStream extends OutputStream realOutput.writeByte(TC_OBJECT); writeObject(osc); - if (replaceDone) - assignNewHandle(replacedObject); - else - assignNewHandle(obj); - + if (shared) + if (replaceDone) + assignNewHandle(replacedObject); + else + assignNewHandle(obj); + if (obj instanceof Externalizable) { if (protocolVersion == PROTOCOL_VERSION_2) diff --git a/libjava/classpath/java/io/StreamTokenizer.java b/libjava/classpath/java/io/StreamTokenizer.java index b4695ab3d09..759aa9a2f05 100644 --- a/libjava/classpath/java/io/StreamTokenizer.java +++ b/libjava/classpath/java/io/StreamTokenizer.java @@ -330,6 +330,7 @@ public class StreamTokenizer { while ((ch = in.read()) != '\n' && ch != '\r' && ch != TT_EOF) ; + if (ch != TT_EOF) in.unread(ch); return nextToken(); // Recursive, but not too deep in normal cases @@ -431,6 +432,7 @@ public class StreamTokenizer { while ((ch = in.read()) != '\n' && ch != '\r' && ch != TT_EOF) ; + if (ch != TT_EOF) in.unread(ch); return nextToken(); // Recursive, but not too deep in normal cases. diff --git a/libjava/classpath/java/io/class-dependencies.conf b/libjava/classpath/java/io/class-dependencies.conf deleted file mode 100644 index 633bb174941..00000000000 --- a/libjava/classpath/java/io/class-dependencies.conf +++ /dev/null @@ -1,100 +0,0 @@ -# This property file contains dependencies of classes, methods, and -# field on other methods or classes. -# -# Syntax: -# -# <used>: <needed 1> [... <needed N>] -# -# means that when <used> is included, <needed 1> (... <needed N>) must -# be included as well. -# -# <needed X> and <used> are of the form -# -# <class.methodOrField(signature)> -# -# or just -# -# <class> -# -# Within dependencies, variables can be used. A variable is defined as -# follows: -# -# {variable}: value1 value2 ... value<n> -# -# variables can be used on the right side of dependencies as follows: -# -# <used>: com.bla.blu.{variable}.Class.m()V -# -# The use of the variable will expand to <n> dependencies of the form -# -# <used>: com.bla.blu.value1.Class.m()V -# <used>: com.bla.blu.value2.Class.m()V -# ... -# <used>: com.bla.blu.value<n>.Class.m()V -# -# Variables can be redefined when building a system to select the -# required support for features like encodings, protocols, etc. -# -# Hints: -# -# - For methods and fields, the signature is mandatory. For -# specification, please see the Java Virtual Machine Specification by -# SUN. Unlike in the spec, field signatures (types) are in brackets. -# -# - Package names must be separated by '/' (and not '.'). E.g., -# java/lang/Class (this is necessary, because the '.' is used to -# separate method or field names from classes) -# -# - In case <needed> refers to a class, only the class itself will be -# included in the resulting binary, NOT necessarily all its methods -# and fields. If you want to refer to all methods and fields, you can -# write class.* as an abbreviation. -# -# - Abbreviations for packages are also possible: my/package/* means all -# methods and fields of all classes in my/package. -# -# - A line with a trailing '\' continues in the next line. - -java/io/File: \ - java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ - java/lang/InternalError.<init>(Ljava/lang/String;)V \ - java/io/IOException.<init>(Ljava/lang/String;)V \ - java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V - -java/io/FileDescriptor: \ - java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ - java/lang/InternalError.<init>(Ljava/lang/String;)V \ - java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V \ - java/io/IOException.<init>(Ljava/lang/String;)V - -java/io/FileInputStream: \ - java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ - java/lang/InternalError.<init>(Ljava/lang/String;)V \ - java/io/IOException.<init>(Ljava/lang/String;)V \ - java/io/FileNotFoundException.<init>(Ljava/lang/String;)V - -java/io/FileOutputStream: \ - java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ - java/lang/InternalError.<init>(Ljava/lang/String;)V \ - java/io/FileNotFoundException.<init>(Ljava/lang/String;)V \ - java/io/IOException.<init>(Ljava/lang/String;)V - -java/io/ObjectInputStream: \ - java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ - java/lang/InternalError.<init>(Ljava/lang/String;)V \ - java/lang/SecurityManager.currentClassLoader()Ljava/lang/ClassLoader; \ - java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V - -java/io/ObjectOutputStream: \ - java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ - java/lang/InternalError.<init>(Ljava/lang/String;)V \ - java/lang/SecurityManager.currentClassLoader()Ljava/lang/ClassLoader; \ - java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V - -java/io/RandomAccessFile: \ - java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ - java/lang/InternalError.<init>(Ljava/lang/String;)V \ - java/io/FileNotFoundException.<init>(Ljava/lang/String;)V \ - java/io/IOException.<init>(Ljava/lang/String;)V - -# end of file diff --git a/libjava/classpath/java/lang/Character.java b/libjava/classpath/java/lang/Character.java index b9c6f24e79f..506033f31bc 100644 --- a/libjava/classpath/java/lang/Character.java +++ b/libjava/classpath/java/lang/Character.java @@ -156,7 +156,7 @@ public final class Character implements Serializable, Comparable<Character> private final String canonicalName; /** Enumeration for the <code>forName()</code> method */ - private enum NameType { CANONICAL, NO_SPACES, CONSTANT; }; + private enum NameType { CANONICAL, NO_SPACES, CONSTANT; } /** * Constructor for strictly defined blocks. diff --git a/libjava/classpath/java/lang/Class.java b/libjava/classpath/java/lang/Class.java index f44782f9692..d3df881367c 100644 --- a/libjava/classpath/java/lang/Class.java +++ b/libjava/classpath/java/lang/Class.java @@ -45,6 +45,7 @@ import java.io.InputStream; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.annotation.Inherited; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -1126,15 +1127,7 @@ public final class Class<T> if (!Modifier.isPublic(constructor.getModifiers()) || !Modifier.isPublic(VMClass.getModifiers(this, true))) { - final Constructor finalConstructor = constructor; - AccessController.doPrivileged(new PrivilegedAction() - { - public Object run() - { - finalConstructor.setAccessible(true); - return null; - } - }); + setAccessible(constructor); } synchronized(this) { @@ -1397,7 +1390,9 @@ public final class Class<T> { try { - return (T[]) getMethod("values").invoke(null); + Method m = getMethod("values"); + setAccessible(m); + return (T[]) m.invoke(null); } catch (NoSuchMethodException exception) { @@ -1787,5 +1782,18 @@ public final class Class<T> return VMClass.isMemberClass(this); } - + /** + * Utility method for use by classes in this package. + */ + static void setAccessible(final AccessibleObject obj) + { + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + obj.setAccessible(true); + return null; + } + }); + } } diff --git a/libjava/classpath/java/lang/Enum.java b/libjava/classpath/java/lang/Enum.java index f141619be4d..fa217bb67a6 100644 --- a/libjava/classpath/java/lang/Enum.java +++ b/libjava/classpath/java/lang/Enum.java @@ -97,9 +97,12 @@ public abstract class Enum<T extends Enum<T>> try { + // XXX We should not use getDeclaredField, because it does + // an unnecessary security check. Field f = etype.getDeclaredField(s); if (! f.isEnumConstant()) throw new IllegalArgumentException(s); + Class.setAccessible(f); return (S) f.get(null); } catch (NoSuchFieldException exception) @@ -220,4 +223,14 @@ public abstract class Enum<T extends Enum<T>> k = k.getSuperclass(); return k; } + + /** + * Enumerations can not have finalization methods. + * + * @since 1.6 + */ + protected final void finalize() + { + } + } diff --git a/libjava/classpath/java/lang/StrictMath.java b/libjava/classpath/java/lang/StrictMath.java index ec74ca4133b..6eb1cb20859 100644 --- a/libjava/classpath/java/lang/StrictMath.java +++ b/libjava/classpath/java/lang/StrictMath.java @@ -2278,8 +2278,9 @@ public final strictfp class StrictMath j |= iq[i]; if (j == 0) // Need recomputation. { - int k; - for (k = 1; iq[jk - k] == 0; k++); // k = no. of terms needed. + int k; // k = no. of terms needed. + for (k = 1; iq[jk - k] == 0; k++) + ; for (i = jz + 1; i <= jz + k; i++) // Add q[jz+1] to q[jz+k]. { diff --git a/libjava/classpath/java/lang/String.java b/libjava/classpath/java/lang/String.java index 28b77c0aa56..ecb46881148 100644 --- a/libjava/classpath/java/lang/String.java +++ b/libjava/classpath/java/lang/String.java @@ -1592,8 +1592,10 @@ public final class String if (begin == limit) return ""; while (value[begin++] <= '\u0020'); + int end = limit; - while (value[--end] <= '\u0020'); + while (value[--end] <= '\u0020') + ; return substring(begin - offset - 1, end - offset + 1); } @@ -1988,4 +1990,17 @@ public final class String return Character.offsetByCodePoints(value, offset, count, offset + index, codePointOffset); } + + /** + * Returns true if, and only if, {@link #length()} + * is <code>0</code>. + * + * @return true if the length of the string is zero. + * @since 1.6 + */ + public boolean isEmpty() + { + return count == 0; + } + } diff --git a/libjava/classpath/java/lang/System.java b/libjava/classpath/java/lang/System.java index ca390bf161b..68d76fc2164 100644 --- a/libjava/classpath/java/lang/System.java +++ b/libjava/classpath/java/lang/System.java @@ -1,5 +1,5 @@ /* System.java -- useful methods to interface with the system - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -42,8 +42,11 @@ package java.lang; import gnu.classpath.SystemProperties; import gnu.classpath.VMStackWalker; +import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; +import java.nio.channels.Channel; +import java.nio.channels.spi.SelectorProvider; import java.util.AbstractCollection; import java.util.Collection; import java.util.Collections; @@ -362,6 +365,7 @@ public final class System * <dt>gnu.java.io.encoding_scheme_alias.iso-latin-_?</dt> <dd>8859_?</dd> * <dt>gnu.java.io.encoding_scheme_alias.latin?</dt> <dd>8859_?</dd> * <dt>gnu.java.io.encoding_scheme_alias.utf-8</dt> <dd>UTF8</dd> + * <dt>gnu.java.util.zoneinfo.dir</dt> <dd>Root of zoneinfo tree</dd> * <dt>gnu.javax.print.server</dt> <dd>Hostname of external CUPS server.</dd> * </dl> * @@ -671,6 +675,25 @@ public final class System return VMRuntime.mapLibraryName(libname); } + /** + * Returns the inherited channel of the VM. + * + * This wraps the inheritedChannel() call of the system's default + * {@link SelectorProvider}. + * + * @return the inherited channel of the VM + * + * @throws IOException If an I/O error occurs + * @throws SecurityException If an installed security manager denies access + * to RuntimePermission("inheritedChannel") + * + * @since 1.5 + */ + public static Channel inheritedChannel() + throws IOException + { + return SelectorProvider.provider().inheritedChannel(); + } /** * This is a specialised <code>Collection</code>, providing diff --git a/libjava/classpath/java/lang/class-dependencies.conf b/libjava/classpath/java/lang/class-dependencies.conf deleted file mode 100644 index 4fbf75eb1ce..00000000000 --- a/libjava/classpath/java/lang/class-dependencies.conf +++ /dev/null @@ -1,58 +0,0 @@ -# This property file contains dependencies of classes, methods, and -# field on other methods or classes. -# -# Syntax: -# -# <used>: <needed 1> [... <needed N>] -# -# means that when <used> is included, <needed 1> (... <needed N>) must -# be included as well. -# -# <needed X> and <used> are of the form -# -# <class.methodOrField(signature)> -# -# or just -# -# <class> -# -# Within dependencies, variables can be used. A variable is defined as -# follows: -# -# {variable}: value1 value2 ... value<n> -# -# variables can be used on the right side of dependencies as follows: -# -# <used>: com.bla.blu.{variable}.Class.m()V -# -# The use of the variable will expand to <n> dependencies of the form -# -# <used>: com.bla.blu.value1.Class.m()V -# <used>: com.bla.blu.value2.Class.m()V -# ... -# <used>: com.bla.blu.value<n>.Class.m()V -# -# Variables can be redefined when building a system to select the -# required support for features like encodings, protocols, etc. -# -# Hints: -# -# - For methods and fields, the signature is mandatory. For -# specification, please see the Java Virtual Machine Specification by -# SUN. Unlike in the spec, field signatures (types) are in brackets. -# -# - Package names must be separated by '/' (and not '.'). E.g., -# java/lang/Class (this is necessary, because the '.' is used to -# separate method or field names from classes) -# -# - In case <needed> refers to a class, only the class itself will be -# included in the resulting binary, NOT necessarily all its methods -# and fields. If you want to refer to all methods and fields, you can -# write class.* as an abbreviation. -# -# - Abbreviations for packages are also possible: my/package/* means all -# methods and fields of all classes in my/package. -# -# - A line with a trailing '\' continues in the next line. - -# end of file diff --git a/libjava/classpath/java/lang/management/LockInfo.java b/libjava/classpath/java/lang/management/LockInfo.java new file mode 100644 index 00000000000..ae5166834ca --- /dev/null +++ b/libjava/classpath/java/lang/management/LockInfo.java @@ -0,0 +1,114 @@ +/* LockInfo.java - Information on a lock. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import java.beans.ConstructorProperties; + +/** + * Provides information on a lock held by a thread. + * A lock can be either a built-in monitor, an + * <emph>ownable synchronizer</emph> (i.e. a subclass + * of {@link java.util.concurrent.locks.AbstractOwnableSynchronizer}), + * or a {@link java.util.concurrent.locks.Condition} + * object. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public class LockInfo +{ + + /** + * The class name of the lock object. + */ + private String className; + + /** + * The identity hash code of the lock object. + */ + private int identityHashCode; + + /** + * Constructs a new {@link LockInfo} object with the + * specified class name and identity hash code. + * + * @param className the name of the class of the lock object. + * @param identityHashCode the identity hash code of the + * lock object. + */ + @ConstructorProperties({"className","identityHashCode"}) + public LockInfo(String className, int identityHashCode) + { + this.className = className; + this.identityHashCode = identityHashCode; + } + + /** + * Returns the class name of the lock object. + * + * @return the class name of the lock object. + */ + public String getClassName() + { + return className; + } + + /** + * Returns the identity hash code of the lock object. + * + * @return the identity hash code of the lock object. + */ + public int getIdentityHashCode() + { + return identityHashCode; + } + + /** + * Returns a textual representation of the lock, + * constructed by concatenating the class name, + * <code>'@'</code> and the identity hash code + * in unsigned hexadecimal form. + * + * @return a textual representation of the lock. + */ + public String toString() + { + return className + '@' + Integer.toHexString(identityHashCode); + } + +} diff --git a/libjava/classpath/java/lang/management/ManagementFactory.java b/libjava/classpath/java/lang/management/ManagementFactory.java index a51ca0f4c9f..977b3997535 100644 --- a/libjava/classpath/java/lang/management/ManagementFactory.java +++ b/libjava/classpath/java/lang/management/ManagementFactory.java @@ -49,20 +49,36 @@ import gnu.java.lang.management.MemoryPoolMXBeanImpl; import gnu.java.lang.management.RuntimeMXBeanImpl; import gnu.java.lang.management.ThreadMXBeanImpl; +import java.io.IOException; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.logging.LogManager; +import javax.management.Attribute; import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; import javax.management.MBeanServerFactory; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; import javax.management.ObjectName; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + /** * <p> * Provides access to the system's management beans via a series @@ -549,4 +565,253 @@ public class ManagementFactory return platformServer; } + /** + * <p> + * Returns a proxy for the specified platform bean. A proxy object is created + * using <code>Proxy.newProxyInstance(mxbeanInterface.getClassLoader(), + * new Class[] { mxbeanInterface }, handler)</code>. The + * {@link javax.management.NotificationEmitter} class is also added to the + * array if the bean provides notifications. <code>handler</code> refers + * to the invocation handler which forwards calls to the connection, and + * also provides translation between the Java data types used in the + * bean interfaces and the open data types, as specified in the description + * of this class. It is this translation that makes the + * usual {@link javax.management.MBeanServerInvocationHandler} inappropriate + * for providing such a proxy. + * </p> + * <p> + * <strong>Note</strong>: use of the proxy may result in + * {@link java.io.IOException}s from the underlying {@link MBeanServerConnection} + * and a {@link java.io.InvalidObjectException} if enum constants + * used on the client and the server don't match. + * </p> + * + * @param connection the server connection to use to access the bean. + * @param mxbeanName the {@link javax.management.ObjectName} of the + * bean to provide a proxy for. + * @param mxbeanInterface the interface for the bean being proxied. + * @return a proxy for the specified bean. + * @throws IllegalArgumentException if <code>mxbeanName</code> is not a valid + * {@link javax.management.ObjectName}, + * the interface and name do not match the + * same bean, the name does not refer to a + * platform bean or the bean is not registered + * with the server accessed by <code>connection</code>. + * @throws IOException if the connection throws one. + */ + public static <T> T newPlatformMXBeanProxy(MBeanServerConnection connection, + String mxbeanName, + Class<T> mxbeanInterface) + throws IOException + { + if (!(mxbeanName.equals(CLASS_LOADING_MXBEAN_NAME) || + mxbeanName.equals(COMPILATION_MXBEAN_NAME) || + mxbeanName.startsWith(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE) || + mxbeanName.startsWith(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE) || + mxbeanName.equals(MEMORY_MXBEAN_NAME) || + mxbeanName.startsWith(MEMORY_POOL_MXBEAN_DOMAIN_TYPE) || + mxbeanName.equals(OPERATING_SYSTEM_MXBEAN_NAME) || + mxbeanName.equals(RUNTIME_MXBEAN_NAME) || + mxbeanName.equals(THREAD_MXBEAN_NAME))) + { + throw new IllegalArgumentException("The named bean, " + mxbeanName + + ", is not a platform name."); + } + if ((mxbeanName.equals(CLASS_LOADING_MXBEAN_NAME) && + mxbeanInterface != ClassLoadingMXBean.class) || + (mxbeanName.equals(COMPILATION_MXBEAN_NAME) && + mxbeanInterface != CompilationMXBean.class) || + (mxbeanName.startsWith(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE) && + mxbeanInterface != GarbageCollectorMXBean.class) || + (mxbeanName.startsWith(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE) && + mxbeanInterface != MemoryManagerMXBean.class) || + (mxbeanName.equals(MEMORY_MXBEAN_NAME) && + mxbeanInterface != MemoryMXBean.class) || + (mxbeanName.startsWith(MEMORY_POOL_MXBEAN_DOMAIN_TYPE) && + mxbeanInterface != MemoryPoolMXBean.class) || + (mxbeanName.equals(OPERATING_SYSTEM_MXBEAN_NAME) && + mxbeanInterface != OperatingSystemMXBean.class) || + (mxbeanName.equals(RUNTIME_MXBEAN_NAME) && + mxbeanInterface != RuntimeMXBean.class) || + (mxbeanName.equals(THREAD_MXBEAN_NAME) && + mxbeanInterface != ThreadMXBean.class)) + throw new IllegalArgumentException("The interface, " + mxbeanInterface + + ", does not match the bean, " + mxbeanName); + ObjectName bean; + try + { + bean = new ObjectName(mxbeanName); + } + catch (MalformedObjectNameException e) + { + throw new IllegalArgumentException("The named bean is invalid."); + } + if (!(connection.isRegistered(bean))) + throw new IllegalArgumentException("The bean is not registered on this connection."); + Class[] interfaces; + if (mxbeanName.equals(MEMORY_MXBEAN_NAME)) + interfaces = new Class[] { mxbeanInterface, NotificationEmitter.class }; + else + interfaces = new Class[] { mxbeanInterface }; + return (T) Proxy.newProxyInstance(mxbeanInterface.getClassLoader(), + interfaces, + new ManagementInvocationHandler(connection, bean)); + } + + /** + * This invocation handler provides method calls for a platform bean + * by forwarding them to a {@link MBeanServerConnection}. Translation from + * Java data types to open data types is performed as specified above. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static class ManagementInvocationHandler + implements InvocationHandler + { + + /** + * The encapsulated connection. + */ + private MBeanServerConnection conn; + + /** + * The bean being proxied. + */ + private ObjectName bean; + + /** + * Constructs a new {@link InvocationHandler} which proxies + * for the specified bean using the supplied connection. + * + * @param conn the connection on which to forward method calls. + * @param bean the bean to proxy. + */ + public ManagementInvocationHandler(MBeanServerConnection conn, + ObjectName bean) + throws IOException + { + this.conn = conn; + this.bean = bean; + } + + /** + * Called by the proxy class whenever a method is called. The method + * is emulated by retrieving an attribute from, setting an attribute on + * or invoking a method on the server connection as required. Translation + * between the Java data types supplied as arguments to the open types used + * by the bean is provided, as well as translation of the return value back + * in to the appropriate Java type. + * + * @param proxy the proxy on which the method was called. + * @param method the method which was called. + * @param args the arguments supplied to the method. + * @return the return value from the method. + * @throws Throwable if an exception is thrown in performing the + * method emulation. + */ + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable + { + String name = method.getName(); + if (name.equals("toString")) + return "Proxy for " + bean + " using " + conn; + if (name.equals("addNotificationListener")) + { + conn.addNotificationListener(bean, + (NotificationListener) args[0], + (NotificationFilter) args[1], + args[2]); + return null; + } + if (name.equals("getNotificationInfo")) + return conn.getMBeanInfo(bean).getNotifications(); + if (name.equals("removeNotificationListener")) + { + if (args.length == 1) + conn.removeNotificationListener(bean, + (NotificationListener) + args[0]); + else + conn.removeNotificationListener(bean, + (NotificationListener) + args[0], + (NotificationFilter) + args[1], args[2]); + return null; + } + String attrib = null; + if (name.startsWith("get")) + attrib = name.substring(3); + else if (name.startsWith("is")) + attrib = name.substring(2); + if (attrib != null) + return translate(conn.getAttribute(bean, attrib), method); + else if (name.startsWith("set")) + { + conn.setAttribute(bean, new Attribute(name.substring(3), + args[0])); + return null; + } + else + return translate(conn.invoke(bean, name, args, null), method); + } + + /** + * Translates the returned open data type to the value + * required by the interface. + * + * @param otype the open type returned by the method call. + * @param method the method that was called. + * @return the equivalent return type required by the interface. + * @throws Throwable if an exception is thrown in performing the + * conversion. + */ + private final Object translate(Object otype, Method method) + throws Throwable + { + Class<?> returnType = method.getReturnType(); + if (returnType.isEnum()) + { + String ename = (String) otype; + Enum[] constants = (Enum[]) returnType.getEnumConstants(); + for (Enum c : constants) + if (c.name().equals(ename)) + return c; + } + if (List.class.isAssignableFrom(returnType)) + { + Object[] elems = (Object[]) otype; + List l = new ArrayList(elems.length); + for (Object elem : elems) + l.add(elem); + return l; + } + if (Map.class.isAssignableFrom(returnType)) + { + TabularData data = (TabularData) otype; + Map m = new HashMap(data.size()); + for (Object val : data.values()) + { + CompositeData vals = (CompositeData) val; + m.put(vals.get("key"), vals.get("value")); + } + return m; + } + try + { + Method m = returnType.getMethod("from", + new Class[] + { CompositeData.class }); + return m.invoke(null, (CompositeData) otype); + } + catch (NoSuchMethodException e) + { + /* Ignored; we expect this if this + isn't a from(CompositeData) class */ + } + return otype; + } + + } } diff --git a/libjava/classpath/java/lang/management/MemoryUsage.java b/libjava/classpath/java/lang/management/MemoryUsage.java index 3c2a4cba6e9..d851da9a72e 100644 --- a/libjava/classpath/java/lang/management/MemoryUsage.java +++ b/libjava/classpath/java/lang/management/MemoryUsage.java @@ -180,14 +180,14 @@ public class MemoryUsage if (data == null) return null; CompositeType type = data.getCompositeType(); - ThreadInfo.checkAttribute(type, "init", SimpleType.LONG); - ThreadInfo.checkAttribute(type, "used", SimpleType.LONG); - ThreadInfo.checkAttribute(type, "committed", SimpleType.LONG); - ThreadInfo.checkAttribute(type, "max", SimpleType.LONG); - return new MemoryUsage(((Long) data.get("init")).longValue(), - ((Long) data.get("used")).longValue(), - ((Long) data.get("committed")).longValue(), - ((Long) data.get("max")).longValue()); + ThreadInfo.checkAttribute(type, "Init", SimpleType.LONG); + ThreadInfo.checkAttribute(type, "Used", SimpleType.LONG); + ThreadInfo.checkAttribute(type, "Committed", SimpleType.LONG); + ThreadInfo.checkAttribute(type, "Max", SimpleType.LONG); + return new MemoryUsage(((Long) data.get("Init")).longValue(), + ((Long) data.get("Used")).longValue(), + ((Long) data.get("Committed")).longValue(), + ((Long) data.get("Max")).longValue()); } /** diff --git a/libjava/classpath/java/lang/management/MonitorInfo.java b/libjava/classpath/java/lang/management/MonitorInfo.java new file mode 100644 index 00000000000..cba73a80de4 --- /dev/null +++ b/libjava/classpath/java/lang/management/MonitorInfo.java @@ -0,0 +1,179 @@ +/* MonitorInfo.java - Information on a monitor lock. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.SimpleType; + +/** + * Provides information on a monitor lock held by a thread. + * A monitor lock is obtained when a thread enters a synchronized + * block or method. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public class MonitorInfo + extends LockInfo +{ + + /** + * The stack depth at which the lock was obtained. + */ + private int stackDepth; + + /** + * The stack frame at which the lock was obtained. + */ + private StackTraceElement stackFrame; + + /** + * Constructs a new {@link MonitorInfo} using the specified + * lock class name and identity hash code, and the given + * stack depth and frame. + * + * @param className the class name of the lock object. + * @param identityHashCode the identity hash code of the lock object. + * @param stackDepth the depth of the stack at which the lock + * was obtained. + * @param stackFrame the frame of the stack at which the lock was + * obtained. + * @throws IllegalArgumentException if the stack depth and frame are + * inconsistent i.e. the frame is + * <code>null</code> but the depth is + * ≥ 0, or the frame is not + * <code>null</code> but the depth is + * < 0. + */ + public MonitorInfo(String className, int identityHashCode, int stackDepth, + StackTraceElement stackFrame) + { + super(className, identityHashCode); + if (stackFrame == null && stackDepth >= 0) + throw new IllegalArgumentException("The stack frame is null, but the " + + "stack depth is greater than or equal " + + "to zero."); + if (stackFrame != null && stackDepth < 0) + throw new IllegalArgumentException("The stack frame is not null, but the " + + "stack depth is less than zero."); + this.stackDepth = stackDepth; + this.stackFrame = stackFrame; + } + + /** + * <p> + * Returns a {@link MonitorInfo} instance using the values + * given in the supplied + * {@link javax.management.openmbean.CompositeData} object. + * The composite data instance should contain the following + * attributes with the specified types: + * </p> + * <table> + * <th><td>Name</td><td>Type</td></th> + * <tr><td>className</td><td>java.lang.String</td></tr> + * <tr><td>identityHashCode</td><td>java.lang.Integer</td></tr> + * <tr><td>lockedStackDepth</td><td>java.lang.Integer</td></tr> + * <tr><td>lockedStackFrame</td><td>javax.management.openmbean.CompositeData + * </td></tr> + * </table> + * <p> + * The stack trace is further described as: + * </p> + * <table> + * <th><td>Name</td><td>Type</td></th> + * <tr><td>className</td><td>java.lang.String</td></tr> + * <tr><td>methodName</td><td>java.lang.String</td></tr> + * <tr><td>fileName</td><td>java.lang.String</td></tr> + * <tr><td>lineNumber</td><td>java.lang.Integer</td></tr> + * <tr><td>nativeMethod</td><td>java.lang.Boolean</td></tr> + * </table> + * + * @param data the composite data structure to take values from. + * @return a new instance containing the values from the + * composite data structure, or <code>null</code> + * if the data structure was also <code>null</code>. + * @throws IllegalArgumentException if the composite data structure + * does not match the structure + * outlined above. + */ + public static MonitorInfo from(CompositeData data) + { + if (data == null) + return null; + CompositeType type = data.getCompositeType(); + ThreadInfo.checkAttribute(type, "ClassName", SimpleType.STRING); + ThreadInfo.checkAttribute(type, "IdentityHashCode", SimpleType.INTEGER); + ThreadInfo.checkAttribute(type, "LockedStackDepth", SimpleType.INTEGER); + ThreadInfo.checkAttribute(type, "LockedStackFrame", + ThreadInfo.getStackTraceType()); + CompositeData frame = (CompositeData) data.get("LockedStackFrame"); + return new MonitorInfo((String) data.get("ClassName"), + (Integer) data.get("IdentityHashCode"), + (Integer) data.get("LockedStackDepth"), + new StackTraceElement((String) frame.get("ClassName"), + (String) frame.get("MethodName"), + (String) frame.get("FileName"), + (Integer) frame.get("LineNumber"))); + } + + /** + * Returns the depth of the stack at which the lock was obtained. + * This works as an index into the array returned by + * {@link ThreadInfo#getStackTrace()}. + * + * @return the depth of the stack at which the lock was obtained, + * or a negative number if this information is unavailable. + */ + public int getLockedStackDepth() + { + return stackDepth; + } + + /** + * Returns the stack frame at which the lock was obtained. + * + * @return the stack frame at which the lock was obtained, + * or <code>null</code> if this informati0on is unavailable. + */ + public StackTraceElement getLockedStackFrame() + { + return stackFrame; + } + +} diff --git a/libjava/classpath/java/lang/management/OperatingSystemMXBean.java b/libjava/classpath/java/lang/management/OperatingSystemMXBean.java index 2430a9fbf5c..ed38285a560 100644 --- a/libjava/classpath/java/lang/management/OperatingSystemMXBean.java +++ b/libjava/classpath/java/lang/management/OperatingSystemMXBean.java @@ -87,6 +87,22 @@ public interface OperatingSystemMXBean String getName(); /** + * Returns the system load average for the last minute, or -1 + * if this is unavailable. The availability and calculation + * of the load average is system-dependent, but is usually + * a damped time-dependent average obtained by monitoring the + * number of queued and running processes. It is expected + * that this method will be called frequently to monitor the + * average over time, so it may not be implemented on systems + * where such a call is expensive. + * + * @return the system load average for the last minute, or -1 + * if this is unavailable. + * @since 1.6 + */ + double getSystemLoadAverage(); + + /** * Returns the version of the underlying operating system. This * is equivalent to obtaining the <code>os.version</code> property * via {@link System#getProperty(String)}. diff --git a/libjava/classpath/java/lang/management/ThreadInfo.java b/libjava/classpath/java/lang/management/ThreadInfo.java index 428aca3fac8..884f5af5e9e 100644 --- a/libjava/classpath/java/lang/management/ThreadInfo.java +++ b/libjava/classpath/java/lang/management/ThreadInfo.java @@ -37,6 +37,8 @@ exception statement from your version. */ package java.lang.management; +import java.util.Arrays; + import javax.management.openmbean.ArrayType; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeType; @@ -68,6 +70,8 @@ import javax.management.openmbean.SimpleType; * monitor, upon which the thread described here is blocked.</li> * <li>The stack trace of the thread (if requested on creation * of this object</li> + * <li>The current locks held on object monitors by the thread.</li> + * <li>The current locks held on ownable synchronizers by the thread.</li> * </ul> * <li><strong>Synchronization Statistics</strong> * <ul> @@ -165,11 +169,28 @@ public class ThreadInfo private StackTraceElement[] trace; /** + * The array of information on monitors locked by the thread. + */ + private MonitorInfo[] lockedMonitors; + + /** + * The array of information on ownable synchronizers locked + * by the thread. + */ + private LockInfo[] lockedSynchronizers; + + /** * Cache a local reference to the thread management bean. */ private static ThreadMXBean bean = null; /** + * Cache the {@link javax.management.openmbean.CompositeType} + * for the {@link StackTraceElement}. + */ + private static CompositeType seType; + + /** * Constructs a new {@link ThreadInfo} corresponding * to the thread specified. * @@ -200,13 +221,57 @@ public class ThreadInfo long waitedTime, boolean isInNative, boolean isSuspended, StackTraceElement[] trace) { + this(thread, blockedCount, blockedTime, lock, lockOwner, waitedCount, + waitedTime, isInNative, isSuspended, trace, new MonitorInfo[]{}, + new LockInfo[]{}); + } + + /** + * Constructs a new {@link ThreadInfo} corresponding + * to the thread specified. + * + * @param thread the thread on which the new instance + * will be based. + * @param blockedCount the number of times the thread + * has been blocked. + * @param blockedTime the accumulated number of milliseconds + * the specified thread has been blocked + * (only used with contention monitoring enabled) + * @param lock the monitor lock the thread is waiting for + * (only used if blocked) + * @param lockOwner the thread which owns the monitor lock, or + * <code>null</code> if it doesn't have an owner + * (only used if blocked) + * @param waitedCount the number of times the thread has been in a + * waiting state. + * @param waitedTime the accumulated number of milliseconds the + * specified thread has been waiting + * (only used with contention monitoring enabled) + * @param isInNative true if the thread is in a native method. + * @param isSuspended true if the thread is suspended. + * @param trace the stack trace of the thread to a pre-determined + * depth (see VMThreadMXBeanImpl) + * @param lockedMonitors an array of {@link MonitorInfo} objects + * representing locks held on object monitors + * by the thread. + * @param lockedSynchronizers an array of {@link LockInfo} objects + * representing locks held on ownable + * synchronizers by the thread. + * @since 1.6 + */ + private ThreadInfo(Thread thread, long blockedCount, long blockedTime, + Object lock, Thread lockOwner, long waitedCount, + long waitedTime, boolean isInNative, boolean isSuspended, + StackTraceElement[] trace, MonitorInfo[] lockedMonitors, + LockInfo[] lockedSynchronizers) + { this(thread.getId(), thread.getName(), thread.getState(), blockedCount, blockedTime, lock == null ? null : lock.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(lock)), lockOwner == null ? -1 : lockOwner.getId(), lockOwner == null ? null : lockOwner.getName(), waitedCount, waitedTime, isInNative, isSuspended, - trace); + trace, lockedMonitors, lockedSynchronizers); } /** @@ -248,6 +313,59 @@ public class ThreadInfo long waitedTime, boolean isInNative, boolean isSuspended, StackTraceElement[] trace) { + this(threadId, threadName, threadState, blockedCount, blockedTime, + lockName, lockOwnerId, lockOwnerName, waitedCount, waitedTime, + isInNative, isSuspended, trace, new MonitorInfo[]{}, new LockInfo[]{}); + } + + /** + * Constructs a new {@link ThreadInfo} corresponding + * to the thread details specified. + * + * @param threadId the id of the thread on which this + * new instance will be based. + * @param threadName the name of the thread on which + * this new instance will be based. + * @param threadState the state of the thread on which + * this new instance will be based. + * @param blockedCount the number of times the thread + * has been blocked. + * @param blockedTime the accumulated number of milliseconds + * the specified thread has been blocked + * (only used with contention monitoring enabled) + * @param lockName the name of the monitor lock the thread is waiting for + * (only used if blocked) + * @param lockOwnerId the id of the thread which owns the monitor + * lock, or <code>-1</code> if it doesn't have an owner + * (only used if blocked) + * @param lockOwnerName the name of the thread which owns the monitor + * lock, or <code>null</code> if it doesn't have an + * owner (only used if blocked) + * @param waitedCount the number of times the thread has been in a + * waiting state. + * @param waitedTime the accumulated number of milliseconds the + * specified thread has been waiting + * (only used with contention monitoring enabled) + * @param isInNative true if the thread is in a native method. + * @param isSuspended true if the thread is suspended. + * @param trace the stack trace of the thread to a pre-determined + * depth (see VMThreadMXBeanImpl) + * @param lockedMonitors an array of {@link MonitorInfo} objects + * representing locks held on object monitors + * by the thread. + * @param lockedSynchronizers an array of {@link LockInfo} objects + * representing locks held on ownable + * synchronizers by the thread. + * + * @since 1.6 + */ + private ThreadInfo(long threadId, String threadName, Thread.State threadState, + long blockedCount, long blockedTime, String lockName, + long lockOwnerId, String lockOwnerName, long waitedCount, + long waitedTime, boolean isInNative, boolean isSuspended, + StackTraceElement[] trace, MonitorInfo[] lockedMonitors, + LockInfo[] lockedSynchronizers) + { this.threadId = threadId; this.threadName = threadName; this.threadState = threadState; @@ -261,6 +379,8 @@ public class ThreadInfo this.isInNative = isInNative; this.isSuspended = isSuspended; this.trace = trace; + this.lockedMonitors = lockedMonitors; + this.lockedSynchronizers = lockedSynchronizers; } /** @@ -287,6 +407,44 @@ public class ThreadInfo } /** + * Returns the {@link javax.management.openmbean.CompositeType} for + * a {@link StackTraceElement}. + * + * @return the type for the stack trace element. + */ + static CompositeType getStackTraceType() + { + if (seType == null) + try + { + seType = new CompositeType(StackTraceElement.class.getName(), + "An element of a stack trace", + new String[] { "className", "methodName", + "fileName", "lineNumber", + "nativeMethod" + }, + new String[] { "Name of the class", + "Name of the method", + "Name of the source code file", + "Line number", + "True if this is a native method" + }, + new OpenType[] { + SimpleType.STRING, SimpleType.STRING, + SimpleType.STRING, SimpleType.INTEGER, + SimpleType.BOOLEAN + }); + } + catch (OpenDataException e) + { + throw new IllegalStateException("Something went wrong in creating " + + "the composite data type for the " + + "stack trace element.", e); + } + return seType; + } + + /** * <p> * Returns a {@link ThreadInfo} instance using the values * given in the supplied @@ -336,70 +494,133 @@ public class ThreadInfo if (data == null) return null; CompositeType type = data.getCompositeType(); - checkAttribute(type, "threadId", SimpleType.LONG); - checkAttribute(type, "threadName", SimpleType.STRING); - checkAttribute(type, "threadState", SimpleType.STRING); - checkAttribute(type, "suspended", SimpleType.BOOLEAN); - checkAttribute(type, "inNative", SimpleType.BOOLEAN); - checkAttribute(type, "blockedCount", SimpleType.LONG); - checkAttribute(type, "blockedTime", SimpleType.LONG); - checkAttribute(type, "waitedCount", SimpleType.LONG); - checkAttribute(type, "waitedTime", SimpleType.LONG); - checkAttribute(type, "lockName", SimpleType.STRING); - checkAttribute(type, "lockOwnerId", SimpleType.LONG); - checkAttribute(type, "lockOwnerName", SimpleType.STRING); + checkAttribute(type, "ThreadId", SimpleType.LONG); + checkAttribute(type, "ThreadName", SimpleType.STRING); + checkAttribute(type, "ThreadState", SimpleType.STRING); + checkAttribute(type, "Suspended", SimpleType.BOOLEAN); + checkAttribute(type, "InNative", SimpleType.BOOLEAN); + checkAttribute(type, "BlockedCount", SimpleType.LONG); + checkAttribute(type, "BlockedTime", SimpleType.LONG); + checkAttribute(type, "WaitedCount", SimpleType.LONG); + checkAttribute(type, "WaitedTime", SimpleType.LONG); + checkAttribute(type, "LockName", SimpleType.STRING); + checkAttribute(type, "LockOwnerId", SimpleType.LONG); + checkAttribute(type, "LockOwnerName", SimpleType.STRING); try { - CompositeType seType = - new CompositeType(StackTraceElement.class.getName(), - "An element of a stack trace", - new String[] { "className", "methodName", - "fileName", "lineNumber", - "nativeMethod" - }, - new String[] { "Name of the class", - "Name of the method", - "Name of the source code file", - "Line number", - "True if this is a native method" - }, - new OpenType[] { - SimpleType.STRING, SimpleType.STRING, - SimpleType.STRING, SimpleType.INTEGER, - SimpleType.BOOLEAN - }); - checkAttribute(type, "stackTrace", new ArrayType(1, seType)); + checkAttribute(type, "StackTrace", + new ArrayType(1, getStackTraceType())); + } + catch (OpenDataException e) + { + throw new IllegalStateException("Something went wrong in creating " + + "the array for the stack trace element.", + e); + } + OpenType foundType = type.getType("LockedMonitors"); + if (foundType != null) + try + { + CompositeType mType = new CompositeType(MonitorInfo.class.getName(), + "Information on a object monitor lock", + new String[] { "ClassName", + "IdentityHashCode", + "LockedStackDepth", + "LockedStackFrame" + }, + new String[] { "Name of the class", + "Identity hash code " + + "of the class", + "Stack depth at time " + + "of lock", + "Stack frame at time " + + "of lock", + }, + new OpenType[] { + SimpleType.STRING, SimpleType.INTEGER, + SimpleType.INTEGER, getStackTraceType() + }); + if (!(foundType.equals(new ArrayType(1, mType)))) + throw new IllegalArgumentException("Field LockedMonitors is not of " + + "type " + mType.getClassName()); + } + catch (OpenDataException e) + { + throw new IllegalStateException("Something went wrong in creating " + + "the composite data type for the " + + "object monitor information array.", e); } + foundType = type.getType("LockedSynchronizers"); + if (foundType != null) + try + { + CompositeType lType = new CompositeType(LockInfo.class.getName(), + "Information on a lock", + new String[] { "ClassName", + "IdentityHashCode" + }, + new String[] { "Name of the class", + "Identity hash code " + + "of the class" + }, + new OpenType[] { + SimpleType.STRING, SimpleType.INTEGER + }); + if (!(foundType.equals(new ArrayType(1, lType)))) + throw new IllegalArgumentException("Field LockedSynchronizers is not of " + + "type " + lType.getClassName()); + } catch (OpenDataException e) { throw new IllegalStateException("Something went wrong in creating " + "the composite data type for the " + - "stack trace element.", e); + "ownable synchronizerinformation array.", e); } - CompositeData[] dTraces = (CompositeData[]) data.get("stackTrace"); + CompositeData[] dTraces = (CompositeData[]) data.get("StackTrace"); StackTraceElement[] traces = new StackTraceElement[dTraces.length]; for (int a = 0; a < dTraces.length; ++a) /* FIXME: We can't use the boolean as there is no available constructor. */ traces[a] = - new StackTraceElement((String) dTraces[a].get("className"), - (String) dTraces[a].get("methodName"), - (String) dTraces[a].get("fileName"), + new StackTraceElement((String) dTraces[a].get("ClassName"), + (String) dTraces[a].get("MethodName"), + (String) dTraces[a].get("FileName"), ((Integer) - dTraces[a].get("lineNumber")).intValue()); - return new ThreadInfo(((Long) data.get("threadId")).longValue(), - (String) data.get("threadName"), - Thread.State.valueOf((String) data.get("threadState")), - ((Long) data.get("blockedCount")).longValue(), - ((Long) data.get("blockedTime")).longValue(), - (String) data.get("lockName"), - ((Long) data.get("lockOwnerId")).longValue(), - (String) data.get("lockOwnerName"), - ((Long) data.get("waitedCount")).longValue(), - ((Long) data.get("waitedTime")).longValue(), - ((Boolean) data.get("inNative")).booleanValue(), - ((Boolean) data.get("suspended")).booleanValue(), - traces); + dTraces[a].get("LineNumber")).intValue()); + MonitorInfo[] mInfo; + if (data.containsKey("LockedMonitors")) + { + CompositeData[] dmInfos = (CompositeData[]) data.get("LockedMonitors"); + mInfo = new MonitorInfo[dmInfos.length]; + for (int a = 0; a < dmInfos.length; ++a) + mInfo[a] = MonitorInfo.from(dmInfos[a]); + } + else + mInfo = new MonitorInfo[]{}; + LockInfo[] lInfo; + if (data.containsKey("LockedSynchronizers")) + { + CompositeData[] dlInfos = (CompositeData[]) data.get("LockedSynchronizers"); + lInfo = new LockInfo[dlInfos.length]; + for (int a = 0; a < dlInfos.length; ++a) + lInfo[a] = new LockInfo((String) dlInfos[a].get("ClassName"), + (Integer) dlInfos[a].get("IdentityHashCode")); + } + else + lInfo = new LockInfo[]{}; + return new ThreadInfo(((Long) data.get("ThreadId")).longValue(), + (String) data.get("ThreadName"), + Thread.State.valueOf((String) data.get("ThreadState")), + ((Long) data.get("BlockedCount")).longValue(), + ((Long) data.get("BlockedTime")).longValue(), + (String) data.get("LockName"), + ((Long) data.get("LockOwnerId")).longValue(), + (String) data.get("LockOwnerName"), + ((Long) data.get("WaitedCount")).longValue(), + ((Long) data.get("WaitedTime")).longValue(), + ((Boolean) data.get("InNative")).booleanValue(), + ((Boolean) data.get("Suspended")).booleanValue(), + traces, mInfo, lInfo); } /** @@ -459,9 +680,74 @@ public class ThreadInfo } /** + * Returns an array of {@link MonitorInfo} objects representing + * information on the locks on object monitors held by the thread. + * If no locks are held, or such information was not requested + * on creating this {@link ThreadInfo} object, a zero-length + * array will be returned. + * + * @return information on object monitors locked by this thread. + */ + public MonitorInfo[] getLockedMonitors() + { + return lockedMonitors; + } + + /** + * Returns an array of {@link LockInfo} objects representing + * information on the locks on ownable synchronizers held by the thread. + * If no locks are held, or such information was not requested + * on creating this {@link ThreadInfo} object, a zero-length + * array will be returned. + * + * @return information on ownable synchronizers locked by this thread. + */ + public LockInfo[] getLockedSynchronizers() + { + return lockedSynchronizers; + } + + /** + * <p> + * Returns a {@link LockInfo} object representing the + * lock on which this thread is blocked. If the thread + * is not blocked, this method returns <code>null</code>. + * </p> + * <p> + * The thread may be blocked due to one of three reasons: + * </p> + * <ol> + * <li>The thread is in the <code>BLOCKED</code> state + * waiting to acquire an object monitor in order to enter + * a synchronized method or block.</li> + * <li>The thread is in the <code>WAITING</code> or + * <code>TIMED_WAITING</code> state due to a call to + * {@link java.lang.Object#wait()}.</li> + * <li>The thread is in the <code>WAITING</code> or + * <code>TIMED_WAITING</code> state due to a call + * to {@link java.util.concurrent.locks.LockSupport#park()}. + * The lock is the return value of + * {@link java.util.concurrent.locks.LockSupport#getBlocker()}.</li> + * </ol> + * + * @return a {@link LockInfo} object representing the lock on + * which the thread is blocked, or <code>null</code> if + * the thread isn't blocked. + * @since 1.6 + * @see #getLockName() + */ + public LockInfo getLockInfo() + { + String lockName = getLockName(); + int at = lockName.indexOf('@'); + return new LockInfo(lockName.substring(0, at), + Integer.decode(lockName.substring(at + 1))); + } + + /** * <p> * Returns a {@link java.lang.String} representation of - * the monitor lock on which this thread is blocked. If + * the lock on which this thread is blocked. If * the thread is not blocked, this method returns * <code>null</code>. * </p> @@ -477,7 +763,8 @@ public class ThreadInfo * and * <code>Integer.toHexString(System.identityHashCode(l))</code>. * The value is only unique to the extent that the identity - * hash code is also unique. + * hash code is also unique. The value is the same as would + * be returned by <code>getLockInfo().toString()</code> * </p> * * @return a string representing the lock on which this @@ -486,7 +773,7 @@ public class ThreadInfo */ public String getLockName() { - if (threadState != Thread.State.BLOCKED) + if (!isThreadBlocked()) return null; return lockName; } @@ -504,7 +791,7 @@ public class ThreadInfo */ public long getLockOwnerId() { - if (threadState != Thread.State.BLOCKED) + if (!isThreadBlocked()) return -1; return lockOwnerId; } @@ -522,7 +809,7 @@ public class ThreadInfo */ public String getLockOwnerName() { - if (threadState != Thread.State.BLOCKED) + if (!isThreadBlocked()) return null; return lockOwnerName; } @@ -697,10 +984,40 @@ public class ThreadInfo ", waitedCount=" + waitedCount + ", isInNative=" + isInNative + ", isSuspended=" + isSuspended + - (threadState == Thread.State.BLOCKED ? + (isThreadBlocked() ? ", lockOwnerId=" + lockOwnerId + ", lockOwnerName=" + lockOwnerName : "") + + ", lockedMonitors=" + Arrays.toString(lockedMonitors) + + ", lockedSynchronizers=" + Arrays.toString(lockedSynchronizers) + "]"; } + /** + * <p> + * Returns true if the thread is in a blocked state. + * The thread is regarded as blocked if: + * </p> + * <ol> + * <li>The thread is in the <code>BLOCKED</code> state + * waiting to acquire an object monitor in order to enter + * a synchronized method or block.</li> + * <li>The thread is in the <code>WAITING</code> or + * <code>TIMED_WAITING</code> state due to a call to + * {@link java.lang.Object#wait()}.</li> + * <li>The thread is in the <code>WAITING</code> or + * <code>TIMED_WAITING</code> state due to a call + * to {@link java.util.concurrent.locks.LockSupport#park()}. + * The lock is the return value of + * {@link java.util.concurrent.locks.LockSupport#getBlocker()}.</li> + * </ol> + * + * @return true if the thread is blocked. + */ + private boolean isThreadBlocked() + { + return (threadState == Thread.State.BLOCKED || + threadState == Thread.State.WAITING || + threadState == Thread.State.TIMED_WAITING); + } + } diff --git a/libjava/classpath/java/lang/management/ThreadMXBean.java b/libjava/classpath/java/lang/management/ThreadMXBean.java index 669cb3cd8d2..f73075dfdc2 100644 --- a/libjava/classpath/java/lang/management/ThreadMXBean.java +++ b/libjava/classpath/java/lang/management/ThreadMXBean.java @@ -57,20 +57,30 @@ package java.lang.management; * <p> * This bean supports some optional behaviour, which all * virtual machines may not choose to implement. Specifically, - * this includes the monitoring of the CPU time used by a - * thread, and the monitoring of thread contention. The former - * is further subdivided into the monitoring of either just - * the current thread or all threads. The methods + * this includes the monitoring of: + * </p> + * <ul> + * <li>the CPU time used by a thread</li> + * <li>thread contention</li> + * <li>object monitor usage</li> + * <li>ownable synchronizer usage</li> + * </ul> + * <p> + * The monitoring of CPU time is further subdivided into + * the monitoring of either just the current thread or all + * threads. The methods * {@link #isThreadCpuTimeSupported()}, - * {@link #isCurrentThreadCpuTimeSupported()} and - * {@link #isThreadContentionMonitoringSupported()} may be + * {@link #isCurrentThreadCpuTimeSupported()} + * {@link #isThreadContentionMonitoringSupported()}, + * {@link #isObjectMonitorUsageSupported()} and + * {@link #isSynchronizerUsageSupported()} may be * used to determine whether or not this functionality is * supported. * </p> * <p> - * Furthermore, both these facilities may be disabled. - * In fact, thread contention monitoring is disabled by - * default, and must be explictly turned on by calling + * Furthermore, both time and contention monitoring may be + * disabled. In fact, thread contention monitoring is disabled + * by default, and must be explictly turned on by calling * the {@link #setThreadContentionMonitoringEnabled(boolean)} * method. * </p> @@ -82,6 +92,70 @@ public interface ThreadMXBean { /** + * This method returns information on all live threads at the + * time of execution (some threads may have terminated by the + * time the method completes). This method is simply a shorthand + * for calling {@link #getThreadInfo(long[], boolean, + * boolean)} with the return value of {@link #getAllThreadIds()}. + * + * @param lockedMonitors true if the returned {@link ThreadInfo} + * objects should contain information on + * locked monitors. + * @param lockedSynchronizers true if the returned {@link ThreadInfo} + * objects should contain information + * on locked ownable synchronizers. + * @return an array of {@link ThreadInfo} objects for all live threads. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @throws UnsupportedOperationException if <code>lockedMonitors</code> + * is true, but object monitor + * usage monitoring is not supported + * by the VM, or + * <code>lockedSynchronizers</code> + * is true, but ownable synchronizer + * usage monitoring is not supported + * by the VM. + * @since 1.6 + * @see #getThreadInfo(long[], boolean, boolean) + * @see #getAllThreadIds() + * @see #isObjectMonitorUsageSupported() + * @see #isSynchronizerUsageSupported() + */ + ThreadInfo[] dumpAllThreads(boolean lockedMonitors, + boolean lockedSynchronizers); + + /** + * <p> + * This method obtains a list of threads which are deadlocked + * waiting to obtain monitor or ownable synchronizer ownership. + * This is similar to the behaviour described for + * {@link #getMonitorDeadlockedThreads()}, except this method also + * takes in to account deadlocks involving ownable synchronizers. + * </p> + * <p> + * Note that this method is not designed for controlling + * synchronization, but for troubleshooting problems which cause such + * deadlocks; it may be prohibitively expensive to use in normal + * operation. If only deadlocks involving monitors are of interest, + * then {@link #findMonitorDeadlockedThreads()} should be used in + * preference to this method. + * </p> + * + * @return an array of thread identifiers, corresponding to threads + * which are currently in a deadlocked situation, or + * <code>null</code> if there are no deadlocks. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @throws UnsupportedOperationException if the VM does not support + * the monitoring of ownable + * synchronizer usage. + * @since 1.6 + * @see #findMonitorDeadlockedThreads() + * @see #isSynchronizerUsageSupported() + */ + long[] findDeadlockedThreads(); + + /** * <p> * This method obtains a list of threads which are deadlocked * waiting to obtain monitor ownership. On entering a synchronized @@ -115,13 +189,17 @@ public interface ThreadMXBean * of A and B. Note that this method is not designed for controlling * synchronization, but for troubleshooting problems which cause such * deadlocks; it may be prohibitively expensive to use in normal - * operation. + * operation. This method only returns deadlocks involving monitors; + * to include deadlocks involving ownable synchronizers, + * {@link #findDeadlockedThreads()} should be used instead. * </p> * * @return an array of thread identifiers, corresponding to threads - * which are currently in a deadlocked situation. + * which are currently in a deadlocked situation, or + * <code>null</code> if there are no deadlocks. * @throws SecurityException if a security manager exists and * denies ManagementPermission("monitor"). + * @see #findDeadlockedThreads() */ long[] findMonitorDeadlockedThreads(); @@ -285,6 +363,53 @@ public interface ThreadMXBean ThreadInfo[] getThreadInfo(long[] ids); /** + * Returns information on the specified threads with full + * stack trace information and optional synchronization + * information. If <code>lockedMonitors</code> is false, + * or there are no locked monitors for a particular thread, + * then the corresponding {@link ThreadInfo} object will have + * an empty {@link MonitorInfo} array. Likewise, if + * <code>lockedSynchronizers</code> is false, or there are + * no locked ownable synchronizers for a particular thread, + * then the corresponding {@link ThreadInfo} object will have + * an empty {@link LockInfo} array. If both + * <code>lockedMonitors</code> and <code>lockedSynchronizers</code> + * are false, the return value is equivalent to that from + * <code>{@link #getThreadInfo}(ids, Integer.MAX_VALUE)</code>. + * If an identifier specifies a thread which is either non-existant + * or not alive, then the corresponding element in the returned + * array is <code>null</code>. + * + * @param ids an array of thread identifiers to return information + * on. + * @param lockedMonitors true if information on locked monitors + * should be included. + * @param lockedSynchronizers true if information on locked + * ownable synchronizers should be included. + * @return an array of {@link ThreadInfo} objects matching the + * specified threads. The corresponding element is + * <code>null</code> if the identifier specifies + * a thread that doesn't exist or is not alive. + * @throws IllegalArgumentException if an identifier in the array is + * <= 0. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @throws UnsupportedOperationException if <code>lockedMonitors</code> + * is true, but object monitor + * usage monitoring is not supported + * by the VM, or + * <code>lockedSynchronizers</code> + * is true, but ownable synchronizer + * usage monitoring is not supported + * by the VM. + * @since 1.6 + * @see #isObjectMonitorUsageSupported() + * @see #isSynchronizerUsageSupported() + */ + ThreadInfo[] getThreadInfo(long[] ids, boolean lockedMonitors, + boolean lockedSynchronizers); + + /** * Returns information on the specified thread with * stack trace information to the supplied depth. If the * identifier specifies a thread which is either non-existant @@ -390,6 +515,26 @@ public interface ThreadMXBean boolean isCurrentThreadCpuTimeSupported(); /** + * Returns true if the virtual machine supports the monitoring + * of object monitor usage. + * + * @return true if the monitoring of object monitor usage + * is supported by the virtual machine. + * @since 1.6 + */ + boolean isObjectMonitorUsageSupported(); + + /** + * Returns true if the virtual machine supports the monitoring + * of ownable synchronizer usage. + * + * @return true if the monitoring of ownable synchronizer usage + * is supported by the virtual machine. + * @since 1.6 + */ + boolean isSynchronizerUsageSupported(); + + /** * Returns true if thread contention monitoring is currently * enabled. * diff --git a/libjava/classpath/java/math/BigInteger.java b/libjava/classpath/java/math/BigInteger.java index c897d8bf48d..8d174d084f2 100644 --- a/libjava/classpath/java/math/BigInteger.java +++ b/libjava/classpath/java/math/BigInteger.java @@ -573,7 +573,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> long y_ext = y.words[i - 1] < 0 ? 0xffffffffL : 0; for (; i < x.ival; i++) { - carry += ((long) x.words[i] & 0xffffffffL) + y_ext;; + carry += ((long) x.words[i] & 0xffffffffL) + y_ext; result.words[i] = (int) carry; carry >>>= 32; } @@ -1912,8 +1912,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> private static void setBitOp(BigInteger result, int op, BigInteger x, BigInteger y) { - if (y.words == null) ; - else if (x.words == null || x.ival < y.ival) + if ((y.words != null) && (x.words == null || x.ival < y.ival)) { BigInteger temp = x; x = y; y = temp; op = swappedOp(op); diff --git a/libjava/classpath/java/math/class-dependencies.conf b/libjava/classpath/java/math/class-dependencies.conf deleted file mode 100644 index 4fbf75eb1ce..00000000000 --- a/libjava/classpath/java/math/class-dependencies.conf +++ /dev/null @@ -1,58 +0,0 @@ -# This property file contains dependencies of classes, methods, and -# field on other methods or classes. -# -# Syntax: -# -# <used>: <needed 1> [... <needed N>] -# -# means that when <used> is included, <needed 1> (... <needed N>) must -# be included as well. -# -# <needed X> and <used> are of the form -# -# <class.methodOrField(signature)> -# -# or just -# -# <class> -# -# Within dependencies, variables can be used. A variable is defined as -# follows: -# -# {variable}: value1 value2 ... value<n> -# -# variables can be used on the right side of dependencies as follows: -# -# <used>: com.bla.blu.{variable}.Class.m()V -# -# The use of the variable will expand to <n> dependencies of the form -# -# <used>: com.bla.blu.value1.Class.m()V -# <used>: com.bla.blu.value2.Class.m()V -# ... -# <used>: com.bla.blu.value<n>.Class.m()V -# -# Variables can be redefined when building a system to select the -# required support for features like encodings, protocols, etc. -# -# Hints: -# -# - For methods and fields, the signature is mandatory. For -# specification, please see the Java Virtual Machine Specification by -# SUN. Unlike in the spec, field signatures (types) are in brackets. -# -# - Package names must be separated by '/' (and not '.'). E.g., -# java/lang/Class (this is necessary, because the '.' is used to -# separate method or field names from classes) -# -# - In case <needed> refers to a class, only the class itself will be -# included in the resulting binary, NOT necessarily all its methods -# and fields. If you want to refer to all methods and fields, you can -# write class.* as an abbreviation. -# -# - Abbreviations for packages are also possible: my/package/* means all -# methods and fields of all classes in my/package. -# -# - A line with a trailing '\' continues in the next line. - -# end of file diff --git a/libjava/classpath/java/net/MimeTypeMapper.java b/libjava/classpath/java/net/MimeTypeMapper.java index 8153694b483..b0d400cedbf 100644 --- a/libjava/classpath/java/net/MimeTypeMapper.java +++ b/libjava/classpath/java/net/MimeTypeMapper.java @@ -232,7 +232,8 @@ class MimeTypeMapper implements FileNameMap /** * The MIME types above are put into this Hashtable for faster lookup. */ - private Hashtable mime_types = new Hashtable(150); + private Hashtable<String, String> mime_types + = new Hashtable<String, String>(150); /** * Create a new <code>MimeTypeMapper</code> object. @@ -257,7 +258,7 @@ class MimeTypeMapper implements FileNameMap } } - public static void fillFromFile (Map table, String fname) + public static void fillFromFile (Map<String, String> table, String fname) throws IOException { LineNumberReader reader = @@ -325,17 +326,17 @@ class MimeTypeMapper implements FileNameMap */ public static void main(String[] args) throws IOException { - TreeMap map = new TreeMap(); + TreeMap<String, String> map = new TreeMap<String, String>(); // It is fine to hard-code the name here. This is only ever // used by maintainers, who can hack it if they need to re-run // it. fillFromFile(map, "/etc/mime.types"); - Iterator it = map.keySet().iterator(); + Iterator<String> it = map.keySet().iterator(); boolean first = true; while (it.hasNext()) { - String key = (String) it.next(); - String value = (String) map.get(key); + String key = it.next(); + String value = map.get(key); // Put the "," first since it is easier to make correct syntax this way. System.out.println(" " + (first ? " " : ", ") + "{ \"" + key + "\", \"" + value + "\" }"); diff --git a/libjava/classpath/java/net/MulticastSocket.java b/libjava/classpath/java/net/MulticastSocket.java index 2841192db61..114d11f2d3c 100644 --- a/libjava/classpath/java/net/MulticastSocket.java +++ b/libjava/classpath/java/net/MulticastSocket.java @@ -1,5 +1,5 @@ /* MulticastSocket.java -- Class for using multicast sockets - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -337,7 +337,7 @@ public class MulticastSocket extends DatagramSocket /** * Sets the "Time to Live" value for a socket. The value must be between - * 1 and 255. + * 0 and 255, inclusive. * * @param ttl The new TTL value * @@ -350,7 +350,7 @@ public class MulticastSocket extends DatagramSocket if (isClosed()) throw new SocketException("socket is closed"); - if (ttl <= 0 || ttl > 255) + if (ttl < 0 || ttl > 255) throw new IllegalArgumentException("Invalid ttl: " + ttl); getImpl().setTimeToLive(ttl); diff --git a/libjava/classpath/java/net/NetworkInterface.java b/libjava/classpath/java/net/NetworkInterface.java index 6c78ead5b4c..a3a6058afa1 100644 --- a/libjava/classpath/java/net/NetworkInterface.java +++ b/libjava/classpath/java/net/NetworkInterface.java @@ -40,12 +40,8 @@ package java.net; import gnu.classpath.SystemProperties; -import java.util.Collection; -import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; import java.util.Iterator; -import java.util.Map; import java.util.Vector; /** @@ -100,7 +96,8 @@ public final class NetworkInterface public Enumeration<InetAddress> getInetAddresses() { SecurityManager s = System.getSecurityManager(); - Vector inetAddresses = new Vector(netif.addresses); + Vector<InetAddress> inetAddresses + = new Vector<InetAddress>(netif.addresses); if (s == null) return inetAddresses.elements(); diff --git a/libjava/classpath/java/net/Proxy.java b/libjava/classpath/java/net/Proxy.java index 6f9f1b65be5..315177413ad 100644 --- a/libjava/classpath/java/net/Proxy.java +++ b/libjava/classpath/java/net/Proxy.java @@ -57,7 +57,7 @@ public class Proxy * For compatability with Sun's JDK */ private static final long serialVersionUID = -2231209257930100533L; - }; + } public static final Proxy NO_PROXY = new Proxy(Type.DIRECT, null); diff --git a/libjava/classpath/java/net/ResolverCache.java b/libjava/classpath/java/net/ResolverCache.java index f8790666a0a..d57df49199c 100644 --- a/libjava/classpath/java/net/ResolverCache.java +++ b/libjava/classpath/java/net/ResolverCache.java @@ -97,12 +97,12 @@ class ResolverCache /** * The cache itself. */ - private static HashMap cache = new HashMap(); + private static HashMap<Object, Entry> cache = new HashMap<Object, Entry>(); /** * List of entries which may expire. */ - private static LinkedList killqueue = new LinkedList(); + private static LinkedList<Entry> killqueue = new LinkedList<Entry>(); /** * Return the hostname for the specified IP address. diff --git a/libjava/classpath/java/net/ServerSocket.java b/libjava/classpath/java/net/ServerSocket.java index d5f2a176b81..9cefd29e6b1 100644 --- a/libjava/classpath/java/net/ServerSocket.java +++ b/libjava/classpath/java/net/ServerSocket.java @@ -85,9 +85,7 @@ public class ServerSocket * This constructor is only used by java.nio. */ - // FIXME: Workaround a bug in gcj. - //ServerSocket (PlainSocketImpl impl) throws IOException - ServerSocket(SocketImpl impl) throws IOException + ServerSocket(PlainSocketImpl impl) throws IOException { if (impl == null) throw new NullPointerException("impl may not be null"); @@ -101,8 +99,6 @@ public class ServerSocket * This method is only used by java.nio. */ - // FIXME: Workaround a bug in gcj. - //PlainSocketImpl getImpl() SocketImpl getImpl() { return impl; @@ -390,6 +386,7 @@ public class ServerSocket impl.accept(socket.impl); socket.bound = true; + socket.implCreated = true; SecurityManager sm = System.getSecurityManager(); if (sm != null) diff --git a/libjava/classpath/java/net/Socket.java b/libjava/classpath/java/net/Socket.java index f4f25fe1c1b..64805274241 100644 --- a/libjava/classpath/java/net/Socket.java +++ b/libjava/classpath/java/net/Socket.java @@ -1,5 +1,5 @@ /* Socket.java -- Client socket implementation - Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2006 + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2006, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -83,6 +83,12 @@ public class Socket SocketImpl impl; /** + * True if impl.create() has been called. + */ + // package-private because ServerSocket.implAccept() needs to access it. + boolean implCreated; + + /** * True if the socket is bound. * Package private so it can be set from ServerSocket when accept is called. */ @@ -326,11 +332,23 @@ public class Socket private SocketImpl getImpl() throws SocketException { + if (! implCreated) + { + try + { + impl.create(true); + } + catch (IOException x) + { + throw (SocketException) new SocketException().initCause(x); + } + implCreated = true; + } return impl; } /** - * Binds the socket to the givent local address/port + * Binds the socket to the given local address/port * * @param bindpoint The address/port to bind to * @@ -359,7 +377,6 @@ public class Socket // bind to address/port try { - getImpl().create(true); getImpl().bind(tmp.getAddress(), tmp.getPort()); bound = true; } diff --git a/libjava/classpath/java/net/URI.java b/libjava/classpath/java/net/URI.java index 689843c0bf9..43b10fc4195 100644 --- a/libjava/classpath/java/net/URI.java +++ b/libjava/classpath/java/net/URI.java @@ -693,7 +693,7 @@ public final class URI String portStr = getURIGroup(matcher, AUTHORITY_PORT_GROUP); - if (portStr != null) + if (portStr != null && ! portStr.isEmpty()) try { port = Integer.parseInt(portStr); diff --git a/libjava/classpath/java/net/URL.java b/libjava/classpath/java/net/URL.java index 8f72d0687e4..16a606fbc7e 100644 --- a/libjava/classpath/java/net/URL.java +++ b/libjava/classpath/java/net/URL.java @@ -190,7 +190,8 @@ public final class URL implements Serializable * This a table where we cache protocol handlers to avoid the overhead * of looking them up each time. */ - private static HashMap ph_cache = new HashMap(); + private static HashMap<String, URLStreamHandler> ph_cache + = new HashMap<String, URLStreamHandler>(); /** * Whether or not to cache protocol handlers. @@ -901,7 +902,7 @@ public final class URL implements Serializable // First, see if a protocol handler is in our cache. if (cache_handlers) { - if ((ph = (URLStreamHandler) ph_cache.get(protocol)) != null) + if ((ph = ph_cache.get(protocol)) != null) return ph; } @@ -934,9 +935,9 @@ public final class URL implements Serializable // Cache the systemClassLoader if (systemClassLoader == null) { - systemClassLoader = (ClassLoader) AccessController.doPrivileged - (new PrivilegedAction() { - public Object run() + systemClassLoader = AccessController.doPrivileged + (new PrivilegedAction<ClassLoader>() { + public ClassLoader run() { return ClassLoader.getSystemClassLoader(); } diff --git a/libjava/classpath/java/net/URLClassLoader.java b/libjava/classpath/java/net/URLClassLoader.java index 7e2353ac27e..6df2818c5c9 100644 --- a/libjava/classpath/java/net/URLClassLoader.java +++ b/libjava/classpath/java/net/URLClassLoader.java @@ -145,7 +145,7 @@ public class URLClassLoader extends SecureClassLoader // Instance variables /** Locations to load classes from */ - private final Vector urls = new Vector(); + private final Vector<URL> urls = new Vector<URL>(); /** * Store pre-parsed information for each url into this vector: each @@ -153,7 +153,7 @@ public class URLClassLoader extends SecureClassLoader * attribute which adds to the URLs that will be searched, but this * does not add to the list of urls. */ - private final Vector urlinfos = new Vector(); + private final Vector<URLLoader> urlinfos = new Vector<URLLoader>(); /** Factory used to get the protocol handlers of the URLs */ private final URLStreamHandlerFactory factory; @@ -301,7 +301,6 @@ public class URLClassLoader extends SecureClassLoader if ("file".equals (protocol)) { File dir = new File(file); - URL absUrl; try { absoluteURL = dir.getCanonicalFile().toURL(); @@ -329,12 +328,12 @@ public class URLClassLoader extends SecureClassLoader // First see if we can find a handler with the correct name. try { - Class handler = Class.forName(URL_LOADER_PREFIX + protocol); - Class[] argTypes = new Class[] { URLClassLoader.class, - URLStreamHandlerCache.class, - URLStreamHandlerFactory.class, - URL.class, - URL.class }; + Class<?> handler = Class.forName(URL_LOADER_PREFIX + protocol); + Class<?>[] argTypes = new Class<?>[] { URLClassLoader.class, + URLStreamHandlerCache.class, + URLStreamHandlerFactory.class, + URL.class, + URL.class }; Constructor k = handler.getDeclaredConstructor(argTypes); loader = (URLLoader) k.newInstance(new Object[] { this, @@ -395,7 +394,7 @@ public class URLClassLoader extends SecureClassLoader } urlinfos.add(loader); - ArrayList extra = loader.getClassPath(); + ArrayList<URLLoader> extra = loader.getClassPath(); if (extra != null) urlinfos.addAll(extra); } @@ -602,10 +601,10 @@ public class URLClassLoader extends SecureClassLoader Class result = null; if (sm != null && securityContext != null) { - result = (Class)AccessController.doPrivileged - (new PrivilegedAction() + result = AccessController.doPrivileged + (new PrivilegedAction<Class>() { - public Object run() + public Class run() { return defineClass(className, classData, 0, classData.length, @@ -848,9 +847,9 @@ public class URLClassLoader extends SecureClassLoader + securityContext); URLClassLoader loader = - (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction() + AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() { - public Object run() + public URLClassLoader run() { return new URLClassLoader(parent, (AccessControlContext) securityContext); diff --git a/libjava/classpath/java/net/class-dependencies.conf b/libjava/classpath/java/net/class-dependencies.conf deleted file mode 100644 index 8b130f53684..00000000000 --- a/libjava/classpath/java/net/class-dependencies.conf +++ /dev/null @@ -1,122 +0,0 @@ -# This property file contains dependencies of classes, methods, and -# field on other methods or classes. -# -# Syntax: -# -# <used>: <needed 1> [... <needed N>] -# -# means that when <used> is included, <needed 1> (... <needed N>) must -# be included as well. -# -# <needed X> and <used> are of the form -# -# <class.methodOrField(signature)> -# -# or just -# -# <class> -# -# Within dependencies, variables can be used. A variable is defined as -# follows: -# -# {variable}: value1 value2 ... value<n> -# -# variables can be used on the right side of dependencies as follows: -# -# <used>: com.bla.blu.{variable}.Class.m()V -# -# The use of the variable will expand to <n> dependencies of the form -# -# <used>: com.bla.blu.value1.Class.m()V -# <used>: com.bla.blu.value2.Class.m()V -# ... -# <used>: com.bla.blu.value<n>.Class.m()V -# -# Variables can be redefined when building a system to select the -# required support for features like encodings, protocols, etc. -# -# Hints: -# -# - For methods and fields, the signature is mandatory. For -# specification, please see the Java Virtual Machine Specification by -# SUN. Unlike in the spec, field signatures (types) are in brackets. -# -# - Package names must be separated by '/' (and not '.'). E.g., -# java/lang/Class (this is necessary, because the '.' is used to -# separate method or field names from classes) -# -# - In case <needed> refers to a class, only the class itself will be -# included in the resulting binary, NOT necessarily all its methods -# and fields. If you want to refer to all methods and fields, you can -# write class.* as an abbreviation. -# -# - Abbreviations for packages are also possible: my/package/* means all -# methods and fields of all classes in my/package. -# -# - A line with a trailing '\' continues in the next line. - -java/net/InetAddress: \ - java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ - java/lang/InternalError.<init>(Ljava/lang/String;)V \ - java/net/UnknownHostException.<init>(Ljava/lang/String;)V - -java/net/DatagramSocketImpl: \ - java/net/DatagramSocketImpl.fd(Ljava/io/FileDescriptor;) \ - java/net/DatagramSocketImpl.localPort(I) - -java/net/PlainDatagramSocketImpl: \ - java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ - java/lang/InternalError.<init>(Ljava/lang/String;)V \ - java/io/IOException.<init>(Ljava/lang/String;)V \ - java/io/FileDescriptor.<init>()V \ - java/lang/Boolean.<init>(Z)V \ - java/lang/Integer.<init>(I)V \ - java/net/InetAddress.getByName(Ljava/lang/String;)Ljava/net/InetAddress; \ - java/net/InetAddress.getAddress()[B \ - java/lang/Boolean.booleanValue()Z \ - java/lang/Integer.intValue()I \ - java/net/SocketException.<init>(Ljava/lang/String;)V \ - java/net/DatagramPacket.getData()[B \ - java/net/SocketImpl.address(Ljava/net/InetAddress;) \ - java/net/PlainSocketImpl.native_fd(I) \ - java/net/SocketImpl.fd(Ljava/io/FileDescriptor;) \ - java/net/SocketImpl.address(Ljava/net/InetAddress;) \ - java/net/PlainDatagramSocketImpl.native_fd(I) \ - java/net/SocketImpl.localport(I) \ - java/net/SocketImpl.port(I) - -java/net/PlainSocketImpl: \ - java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ - java/lang/InternalError.<init>(Ljava/lang/String;)V \ - java/io/IOException.<init>(Ljava/lang/String;)V \ - java/io/FileDescriptor.<init>()V \ - java/lang/Boolean.<init>(Z)V \ - java/lang/Integer.<init>(I)V \ - java/net/InetAddress.getByName(Ljava/lang/String;)Ljava/net/InetAddress; \ - java/net/InetAddress.getAddress()[B \ - java/lang/Boolean.booleanValue()Z \ - java/lang/Integer.intValue()I \ - java/net/SocketException.<init>(Ljava/lang/String;)V \ - java/net/DatagramPacket.getData()[B \ - java/net/SocketImpl.address(Ljava/net/InetAddress;) \ - java/net/PlainSocketImpl.native_fd(I) \ - java/net/SocketImpl.fd(Ljava/io/FileDescriptor;) \ - java/net/SocketImpl.address(Ljava/net/InetAddress;) \ - java/net/PlainDatagramSocketImpl.native_fd(I) \ - java/net/SocketImpl.localport(I) \ - java/net/SocketImpl.port(I) - -# All protocols supported are loaded via URL.getURLStreamHandler from -# class gnu.java.net.protocol.<protocol>.Handler. -# -# This introduces a dependency for all protocols. To allow an easy selection -# and addition of protocols, the library variable {protocols} can be set to -# the set of supported protocols. -# -{protocols}: http file jar - -java/net/URL.getURLStreamHandler(Ljava/lang/String;)Ljava/net/URLStreamHandler;: \ - gnu/java/net/protocol/{protocols}/Handler.* \ - com/aicas/java/net/protocol/rom/Handler.* - -# end of file diff --git a/libjava/classpath/java/nio/ByteOrder.java b/libjava/classpath/java/nio/ByteOrder.java index 39a3ff893bd..0e3b9173eb6 100644 --- a/libjava/classpath/java/nio/ByteOrder.java +++ b/libjava/classpath/java/nio/ByteOrder.java @@ -61,7 +61,9 @@ public final class ByteOrder */ public static ByteOrder nativeOrder() { - return (System.getProperty ("gnu.cpu.endian").equals("big") + // Let this fail with an NPE when the property is not set correctly. + // Otherwise we risk that NIO is silently working wrong. + return (System.getProperty("gnu.cpu.endian").equals("big") ? BIG_ENDIAN : LITTLE_ENDIAN); } diff --git a/libjava/classpath/java/nio/channels/spi/SelectorProvider.java b/libjava/classpath/java/nio/channels/spi/SelectorProvider.java index db4e65fe14e..b2fb7bb9c81 100644 --- a/libjava/classpath/java/nio/channels/spi/SelectorProvider.java +++ b/libjava/classpath/java/nio/channels/spi/SelectorProvider.java @@ -40,6 +40,7 @@ package java.nio.channels.spi; import gnu.java.nio.SelectorProviderImpl; import java.io.IOException; +import java.nio.channels.Channel; import java.nio.channels.DatagramChannel; import java.nio.channels.Pipe; import java.nio.channels.ServerSocketChannel; @@ -115,6 +116,32 @@ public abstract class SelectorProvider public abstract SocketChannel openSocketChannel() throws IOException; /** + * Returns the inherited channel of the VM. + * + * @return the inherited channel of the VM + * + * @throws IOException If an I/O error occurs + * @throws SecurityException If an installed security manager denies access + * to RuntimePermission("inheritedChannel") + * + * @since 1.5 + */ + public Channel inheritedChannel() + throws IOException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + { + sm.checkPermission(new RuntimePermission("inheritedChannel")); + } + // Implementation note: The default implementation can't do much more + // than return null. If a VM can provide something more useful it should + // install its own SelectorProvider (maybe a subclass of this one) to + // return something more useful. + return null; + } + + /** * Returns the system-wide default selector provider for this invocation * of the Java virtual machine. * diff --git a/libjava/classpath/java/nio/class-dependencies.conf b/libjava/classpath/java/nio/class-dependencies.conf deleted file mode 100644 index 4fbf75eb1ce..00000000000 --- a/libjava/classpath/java/nio/class-dependencies.conf +++ /dev/null @@ -1,58 +0,0 @@ -# This property file contains dependencies of classes, methods, and -# field on other methods or classes. -# -# Syntax: -# -# <used>: <needed 1> [... <needed N>] -# -# means that when <used> is included, <needed 1> (... <needed N>) must -# be included as well. -# -# <needed X> and <used> are of the form -# -# <class.methodOrField(signature)> -# -# or just -# -# <class> -# -# Within dependencies, variables can be used. A variable is defined as -# follows: -# -# {variable}: value1 value2 ... value<n> -# -# variables can be used on the right side of dependencies as follows: -# -# <used>: com.bla.blu.{variable}.Class.m()V -# -# The use of the variable will expand to <n> dependencies of the form -# -# <used>: com.bla.blu.value1.Class.m()V -# <used>: com.bla.blu.value2.Class.m()V -# ... -# <used>: com.bla.blu.value<n>.Class.m()V -# -# Variables can be redefined when building a system to select the -# required support for features like encodings, protocols, etc. -# -# Hints: -# -# - For methods and fields, the signature is mandatory. For -# specification, please see the Java Virtual Machine Specification by -# SUN. Unlike in the spec, field signatures (types) are in brackets. -# -# - Package names must be separated by '/' (and not '.'). E.g., -# java/lang/Class (this is necessary, because the '.' is used to -# separate method or field names from classes) -# -# - In case <needed> refers to a class, only the class itself will be -# included in the resulting binary, NOT necessarily all its methods -# and fields. If you want to refer to all methods and fields, you can -# write class.* as an abbreviation. -# -# - Abbreviations for packages are also possible: my/package/* means all -# methods and fields of all classes in my/package. -# -# - A line with a trailing '\' continues in the next line. - -# end of file diff --git a/libjava/classpath/java/rmi/activation/ActivationID.java b/libjava/classpath/java/rmi/activation/ActivationID.java index c4bbcd285c5..e1cc8a712e1 100644 --- a/libjava/classpath/java/rmi/activation/ActivationID.java +++ b/libjava/classpath/java/rmi/activation/ActivationID.java @@ -174,7 +174,7 @@ public class ActivationID { out.writeObject(uid); out.writeObject(activator); - }; + } /** * Compare by .equals if both a and b are not null, compare directly if at diff --git a/libjava/classpath/java/rmi/server/UnicastRemoteObject.java b/libjava/classpath/java/rmi/server/UnicastRemoteObject.java index 31bf8802301..6ef3432b567 100644 --- a/libjava/classpath/java/rmi/server/UnicastRemoteObject.java +++ b/libjava/classpath/java/rmi/server/UnicastRemoteObject.java @@ -239,11 +239,12 @@ public class UnicastRemoteObject extends RemoteServer (UnicastServerRef) ((RemoteObject) obj).getRef(); return sref.unexportObject(obj, force); } - else + // FIXME + /* else { - // FIXME ; } + */ return true; } diff --git a/libjava/classpath/java/security/BasicPermission.java b/libjava/classpath/java/security/BasicPermission.java index ef2cc4df094..6296cffea36 100644 --- a/libjava/classpath/java/security/BasicPermission.java +++ b/libjava/classpath/java/security/BasicPermission.java @@ -73,9 +73,8 @@ import java.util.Hashtable; * @since 1.1 * @status updated to 1.4 */ -public abstract class BasicPermission extends java.security.Permission +public abstract class BasicPermission extends Permission implements Serializable - // FIXME extends with fully qualified classname as workaround for gcj 3.3. { /** * Compatible with JDK 1.1+. diff --git a/libjava/classpath/java/security/Permission.java b/libjava/classpath/java/security/Permission.java index 48f4d52a18c..9072d95652e 100644 --- a/libjava/classpath/java/security/Permission.java +++ b/libjava/classpath/java/security/Permission.java @@ -181,7 +181,20 @@ public abstract class Permission implements Guard, Serializable */ public String toString() { - return '(' + getClass().getName() + ' ' + getName() + ' ' - + getActions() + ')'; + StringBuffer string = new StringBuffer(); + + string = string.append('('); + string = string.append(getClass().getName()); + string = string.append(' '); + string = string.append(getName()); + + if (!(getActions().equals(""))) + { + string = string.append(' '); + string = string.append(getActions()); + } + + string = string.append(')'); + return string.toString(); } } // class Permission diff --git a/libjava/classpath/java/security/SecureClassLoader.java b/libjava/classpath/java/security/SecureClassLoader.java index dfc1758b52f..f683f9a700f 100644 --- a/libjava/classpath/java/security/SecureClassLoader.java +++ b/libjava/classpath/java/security/SecureClassLoader.java @@ -99,7 +99,7 @@ public class SecureClassLoader extends ClassLoader * * @since 1.5 */ - protected final Class defineClass(String name, ByteBuffer b, CodeSource cs) + protected final Class<?> defineClass(String name, ByteBuffer b, CodeSource cs) { return super.defineClass(name, b, getProtectionDomain(cs)); } diff --git a/libjava/classpath/java/security/SignatureSpi.java b/libjava/classpath/java/security/SignatureSpi.java index 3b46815eca9..80ecbfd44e0 100644 --- a/libjava/classpath/java/security/SignatureSpi.java +++ b/libjava/classpath/java/security/SignatureSpi.java @@ -136,16 +136,23 @@ public abstract class SignatureSpi * bytes of the given buffer. * * @param input The input buffer. - * @throws SignatureException + * @throws IllegalStateException if the engine is not properly initialized. */ - protected void engineUpdate(ByteBuffer input) throws SignatureException + protected void engineUpdate(ByteBuffer input) { byte[] buf = new byte[4096]; while (input.hasRemaining()) { int l = Math.min(input.remaining(), buf.length); input.get(buf, 0, l); - engineUpdate(buf, 0, l); + try + { + engineUpdate(buf, 0, l); + } + catch (SignatureException se) + { + throw new IllegalStateException(se); + } } } diff --git a/libjava/classpath/java/security/cert/X509CertSelector.java b/libjava/classpath/java/security/cert/X509CertSelector.java index 154ed2e4d98..7a7db086b0a 100644 --- a/libjava/classpath/java/security/cert/X509CertSelector.java +++ b/libjava/classpath/java/security/cert/X509CertSelector.java @@ -1,5 +1,5 @@ /* X509CertSelector.java -- selects X.509 certificates by criteria. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,9 +40,17 @@ package java.security.cert; import gnu.classpath.SystemProperties; import gnu.java.security.OID; +import gnu.java.security.x509.GnuPKIExtension; +import gnu.java.security.x509.ext.CertificatePolicies; +import gnu.java.security.x509.ext.Extension; +import gnu.java.security.x509.ext.GeneralName; +import gnu.java.security.x509.ext.GeneralSubtree; +import gnu.java.security.x509.ext.NameConstraints; +import gnu.java.security.x509.ext.GeneralName.Kind; import java.io.IOException; import java.math.BigInteger; +import java.net.InetAddress; import java.security.KeyFactory; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; @@ -88,6 +96,48 @@ public class X509CertSelector implements CertSelector, Cloneable private static final String SUBJECT_KEY_ID = "2.5.29.14"; private static final String NAME_CONSTRAINTS_ID = "2.5.29.30"; + private static boolean checkOid(int[] oid) + { + return (oid != null && oid.length > 2 && + (oid[0] >= 0 && oid[0] <= 2) && (oid[1] >= 0 && oid[1] <= 39)); + } + + private static GeneralName makeName(int id, String name) throws IOException + { + byte[] nameBytes = null; + GeneralName.Kind kind = GeneralName.Kind.forTag(id); + switch (Kind.forTag(id)) + { + case dNSName: + case rfc822Name: + case uniformResourceIdentifier: + nameBytes = name.getBytes("ASCII"); + break; + + case iPAddress: + InetAddress addr = InetAddress.getByName(name); + nameBytes = addr.getAddress(); + break; + + case registeredId: + OID oid = new OID(name); + nameBytes = oid.getDER(); + break; + + case directoryName: + X500Principal xname = new X500Principal(name); + nameBytes = xname.getEncoded(); + break; + + case ediPartyName: + case x400Address: + case otherName: + throw new IOException("cannot decode string representation of " + + kind); + } + return new GeneralName(kind, nameBytes); + } + private int basicConstraints; private X509Certificate cert; private BigInteger serialNo; @@ -100,14 +150,12 @@ public class X509CertSelector implements CertSelector, Cloneable private OID sigId; private PublicKey subjectKey; private X509EncodedKeySpec subjectKeySpec; - private Set keyPurposeSet; - private List altNames; + private Set<String> keyPurposeSet; + private List<GeneralName> altNames; private boolean matchAllNames; private byte[] nameConstraints; - private Set policy; - - // Constructors. - // ------------------------------------------------------------------------ + private Set<OID> policy; + private List<GeneralName> pathToNames; /** * Creates a new X.509 certificate selector. The new selector will be @@ -119,285 +167,280 @@ public class X509CertSelector implements CertSelector, Cloneable basicConstraints = -1; } - // Instance methods. - // ------------------------------------------------------------------------ - /** - * Returns the certificate criterion, or <code>null</code> if this value - * was not set. + * Add a name to match in the NameConstraints extension. The argument is + * the DER-encoded bytes of a GeneralName structure. + * + * See the method {@link #addSubjectAlternativeName(int, byte[])} for the + * format of the GeneralName structure. * - * @return The certificate. + * @param id The name identifier. Must be between 0 and 8. + * @param name The DER-encoded bytes of the name to match. + * @throws IOException If the name DER is malformed. */ - public X509Certificate getCertificate() + public void addPathToName(int id, byte[] name) throws IOException { - return cert; + GeneralName generalName = new GeneralName(GeneralName.Kind.forTag(id), name); + if (pathToNames == null) + pathToNames = new LinkedList<GeneralName>(); + pathToNames.add(generalName); } /** - * Sets the certificate criterion. If set, only certificates that are - * equal to the certificate passed here will be accepted. + * Add a name to match in the NameConstraints extension. This method will + * only recognize certain types of name that have convenient string + * encodings. For robustness, you should use the {@link + * #addPathToName(int, byte[])} method whenever possible. * - * @param cert The certificate. + * @param id The name identifier. Must be between 0 and 8. + * @param name The name. + * @throws IOException If the name cannot be decoded. */ - public void setCertificate(X509Certificate cert) + public void addPathToName(int id, String name) throws IOException { - this.cert = cert; + GeneralName generalName = makeName(id, name); + if (pathToNames == null) + pathToNames = new LinkedList<GeneralName>(); + pathToNames.add(generalName); } /** - * Returns the serial number criterion, or <code>null</code> if this - * value was not set. + * Add a name, as DER-encoded bytes, to the subject alternative names + * criterion. + * + * The name is a GeneralName structure, which has the ASN.1 format: + * + * <pre> + GeneralName ::= CHOICE { + otherName [0] OtherName, + rfc822Name [1] IA5String, + dNSName [2] IA5String, + x400Address [3] ORAddress, + directoryName [4] Name, + ediPartyName [5] EDIPartyName, + uniformResourceIdentifier [6] IA5String, + iPAddress [7] OCTET STRING, + registeredID [8] OBJECT IDENTIFIER } +</pre> * - * @return The serial number. + * @param id The type of name this is. + * @param name The DER-encoded name. + * @throws IOException If the name is not a valid DER sequence. */ - public BigInteger getSerialNumber() + public void addSubjectAlternativeName(int id, byte[] name) + throws IOException { - return serialNo; + GeneralName generalName = new GeneralName(GeneralName.Kind.forTag(id), name); + if (altNames == null) + altNames = new LinkedList<GeneralName>(); + altNames.add(generalName); } /** - * Sets the serial number of the desired certificate. Only certificates that - * contain this serial number are accepted. + * Add a name to the subject alternative names criterion. This method will + * only recognize certain types of name that have convenient string + * encodings. For robustness, you should use the {@link + * #addSubjectAlternativeName(int, byte[])} method whenever possible. + * + * This method can only decode certain name kinds of names as strings. * - * @param serialNo The serial number. + * @param id The type of name this is. Must be in the range [0,8]. + * @param name The name. + * @throws IOException If the id is out of range, or if the name + * is null. */ - public void setSerialNumber(BigInteger serialNo) + public void addSubjectAlternativeName(int id, String name) + throws IOException { - this.serialNo = serialNo; + GeneralName generalName = makeName(id, name); + if (altNames == null) + altNames = new LinkedList<GeneralName>(); + altNames.add(generalName); } - /** - * Returns the issuer criterion as a string, or <code>null</code> if this - * value was not set. - * - * @return The issuer. - */ - public String getIssuerAsString() + public Object clone() { - if (issuer != null) - return issuer.getName(); - else - return null; + try + { + return super.clone(); + } + catch (CloneNotSupportedException shouldNotHappen) + { + throw new Error(shouldNotHappen); + } } /** - * Returns the issuer criterion as a sequence of DER bytes, or - * <code>null</code> if this value was not set. + * Returns the authority key identifier criterion, or <code>null</code> if + * this value was not set. Note that the byte array is cloned to prevent + * modification. * - * @return The issuer. + * @return The authority key identifier. */ - public byte[] getIssuerAsBytes() throws IOException + public byte[] getAuthorityKeyIdentifier() { - if (issuer != null) - return issuer.getEncoded(); + if (authKeyId != null) + return (byte[]) authKeyId.clone(); else return null; } /** - * Sets the issuer, specified as a string representation of the issuer's - * distinguished name. Only certificates issued by this issuer will - * be accepted. + * Returns the basic constraints criterion, or -1 if this value is not set. * - * @param name The string representation of the issuer's distinguished name. - * @throws IOException If the given name is incorrectly formatted. + * @return The basic constraints. */ - public void setIssuer(String name) throws IOException + public int getBasicConstraints() { - if (name != null) - { - try - { - issuer = new X500Principal(name); - } - catch (IllegalArgumentException iae) - { - throw new IOException(iae.getMessage()); - } - } - else - issuer = null; + return basicConstraints; } /** - * Sets the issuer, specified as the DER encoding of the issuer's - * distinguished name. Only certificates issued by this issuer will - * be accepted. + * Returns the certificate criterion, or <code>null</code> if this value + * was not set. * - * @param name The DER encoding of the issuer's distinguished name. - * @throws IOException If the given name is incorrectly formatted. + * @return The certificate. */ - public void setIssuer(byte[] name) throws IOException + public X509Certificate getCertificate() { - if (name != null) - { - try - { - issuer = new X500Principal(name); - } - catch (IllegalArgumentException iae) - { - throw new IOException(iae.getMessage()); - } - } - else - issuer = null; + return cert; } /** - * Returns the subject criterion as a string, of <code>null</code> if - * this value was not set. + * Returns the date at which certificates must be valid, or <code>null</code> + * if this criterion was not set. * - * @return The subject. + * @return The target certificate valitity date. */ - public String getSubjectAsString() + public Date getCertificateValid() { - if (subject != null) - return subject.getName(); + if (certValid != null) + return (Date) certValid.clone(); else return null; } /** - * Returns the subject criterion as a sequence of DER bytes, or - * <code>null</code> if this value is not set. + * Returns the set of extended key purpose IDs, as an unmodifiable set + * of OID strings. Returns <code>null</code> if this criterion is not + * set. * - * @return The subject. + * @return The set of key purpose OIDs (strings). */ - public byte[] getSubjectAsBytes() throws IOException + public Set<String> getExtendedKeyUsage() { - if (subject != null) - return subject.getEncoded(); + if (keyPurposeSet != null) + return Collections.unmodifiableSet(keyPurposeSet); else return null; } /** - * Sets the subject, specified as a string representation of the - * subject's distinguished name. Only certificates with the given - * subject will be accepted. + * Returns the issuer criterion as a sequence of DER bytes, or + * <code>null</code> if this value was not set. * - * @param name The string representation of the subject's distinguished name. - * @throws IOException If the given name is incorrectly formatted. + * @return The issuer. */ - public void setSubject(String name) throws IOException + public byte[] getIssuerAsBytes() throws IOException { - if (name != null) - { - try - { - subject = new X500Principal(name); - } - catch (IllegalArgumentException iae) - { - throw new IOException(iae.getMessage()); - } - } + if (issuer != null) + return issuer.getEncoded(); else - subject = null; + return null; } /** - * Sets the subject, specified as the DER encoding of the subject's - * distinguished name. Only certificates with the given subject will - * be accepted. + * Returns the issuer criterion as a string, or <code>null</code> if this + * value was not set. * - * @param name The DER encoding of the subject's distinguished name. - * @throws IOException If the given name is incorrectly formatted. + * @return The issuer. */ - public void setSubject(byte[] name) throws IOException + public String getIssuerAsString() { - if (name != null) - { - try - { - subject = new X500Principal(name); - } - catch (IllegalArgumentException iae) - { - throw new IOException(iae.getMessage()); - } - } + if (issuer != null) + return issuer.getName(); else - subject = null; + return null; } /** - * Returns the subject key identifier criterion, or <code>null</code> if - * this value was not set. Note that the byte array is cloned to prevent - * modification. + * Returns the public key usage criterion, or <code>null</code> if this + * value is not set. Note that the array is cloned to prevent modification. * - * @return The subject key identifier. + * @return The public key usage. */ - public byte[] getSubjectKeyIdentifier() + public boolean[] getKeyUsage() { - if (subjectKeyId != null) - return (byte[]) subjectKeyId.clone(); + if (keyUsage != null) + return (boolean[]) keyUsage.clone(); else return null; } /** - * Sets the subject key identifier criterion, or <code>null</code> to clear - * this criterion. Note that the byte array is cloned to prevent modification. + * Returns whether or not all specified alternative names must match. + * If false, a certificate is considered a match if <em>one</em> of the + * specified alternative names matches. * - * @param subjectKeyId The subject key identifier. + * @return true if all names must match. */ - public void setSubjectKeyIdentifier(byte[] subjectKeyId) + public boolean getMatchAllSubjectAltNames() { - this.subjectKeyId = subjectKeyId != null ? (byte[]) subjectKeyId.clone() : - null; + return matchAllNames; } /** - * Returns the authority key identifier criterion, or <code>null</code> if - * this value was not set. Note that the byte array is cloned to prevent + * Returns the name constraints criterion, or <code>null</code> if this + * value is not set. Note that the byte array is cloned to prevent * modification. * - * @return The authority key identifier. + * @return The name constraints. */ - public byte[] getAuthorityKeyIdentifier() + public byte[] getNameConstraints() { - if (authKeyId != null) - return (byte[]) authKeyId.clone(); + if (nameConstraints != null) + return (byte[]) nameConstraints.clone(); else return null; } - /** - * Sets the authority key identifier criterion, or <code>null</code> to clear - * this criterion. Note that the byte array is cloned to prevent modification. - * - * @param authKeyId The authority key identifier. - */ - public void setAuthorityKeyIdentifier(byte[] authKeyId) + public Collection<List<?>> getPathToNames() { - this.authKeyId = authKeyId != null ? (byte[]) authKeyId.clone() : null; - } - - /** - * Returns the date at which certificates must be valid, or <code>null</code> - * if this criterion was not set. - * - * @return The target certificate valitity date. - */ - public Date getCertificateValid() - { - if (certValid != null) - return (Date) certValid.clone(); - else - return null; + if (pathToNames != null) + { + List<List<?>> names = new ArrayList<List<?>>(pathToNames.size()); + for (GeneralName name : pathToNames) + { + List<Object> n = new ArrayList<Object>(2); + n.add(name.kind().tag()); + n.add(name.name()); + names.add(n); + } + + return names; + } + return null; } /** - * Sets the date at which certificates must be valid. Specify - * <code>null</code> to clear this criterion. + * Returns the certificate policy extension that will be matched by this + * selector, or null if the certificate policy will not be matched. * - * @param certValid The certificate validity date. + * @return The policy to be matched, or null. */ - public void setCertificateValid(Date certValid) + public Set<String> getPolicy() { - this.certValid = certValid != null ? (Date) certValid.clone() : null; + Set<OID> p = this.policy; + if (p != null) + { + Set<String> strings = new HashSet<String>(p.size()); + for (OID o : p) + { + strings.add(o.toString()); + } + return strings; + } + return null; } /** @@ -418,59 +461,83 @@ public class X509CertSelector implements CertSelector, Cloneable } /** - * This method, and its related X.509 certificate extension — the - * private key usage period — is not supported under the Internet - * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this - * method is not supported either. + * Returns the serial number criterion, or <code>null</code> if this + * value was not set. * - * <p>Do not use this method. It is not deprecated, as it is not deprecated - * in the Java standard, but it is basically a no-operation. + * @return The serial number. + */ + public BigInteger getSerialNumber() + { + return serialNo; + } + + /** + * Get the subject alternative names criterion. The collection returned + * is a collection of pairs: the first element is an {@link Integer} + * containing the name type, and the second is a byte array containing + * the DER-encoded name bytes. * - * @param UNUSED Is silently ignored. + * @return The subject alternative names criterion. Returns null if this + * criterion is not set. */ - public void setPrivateKeyValid(Date UNUSED) + public Collection<List<?>> getSubjectAlternativeNames() { + if (altNames != null) + { + List<List<?>> names = new ArrayList<List<?>>(altNames.size()); + for (GeneralName name : altNames) + { + List<Object> n = new ArrayList<Object>(2); + n.add(name.kind().tag()); + n.add(name.name()); + names.add(n); + } + return names; + } + return null; } /** - * Returns the public key algorithm ID that matching certificates must have, - * or <code>null</code> if this criterion was not set. + * Returns the subject criterion as a sequence of DER bytes, or + * <code>null</code> if this value is not set. * - * @return The public key algorithm ID. + * @return The subject. */ - public String getSubjectPublicKeyAlgID() + public byte[] getSubjectAsBytes() throws IOException { - return String.valueOf(sigId); + if (subject != null) + return subject.getEncoded(); + else + return null; } /** - * Sets the public key algorithm ID that matching certificates must have. - * Specify <code>null</code> to clear this criterion. + * Returns the subject criterion as a string, of <code>null</code> if + * this value was not set. * - * @param sigId The public key ID. - * @throws IOException If the specified ID is not a valid object identifier. + * @return The subject. */ - public void setSubjectPublicKeyAlgID(String sigId) throws IOException + public String getSubjectAsString() { - if (sigId != null) - { - try - { - OID oid = new OID(sigId); - int[] comp = oid.getIDs(); - if (!checkOid(comp)) - throw new IOException("malformed OID: " + sigId); - this.sigId = oid; - } - catch (IllegalArgumentException iae) - { - IOException ioe = new IOException("malformed OID: " + sigId); - ioe.initCause(iae); - throw ioe; - } - } + if (subject != null) + return subject.getName(); else - this.sigId = null; + return null; + } + + /** + * Returns the subject key identifier criterion, or <code>null</code> if + * this value was not set. Note that the byte array is cloned to prevent + * modification. + * + * @return The subject key identifier. + */ + public byte[] getSubjectKeyIdentifier() + { + if (subjectKeyId != null) + return (byte[]) subjectKeyId.clone(); + else + return null; } /** @@ -485,101 +552,282 @@ public class X509CertSelector implements CertSelector, Cloneable } /** - * Sets the subject public key criterion as an opaque representation. - * Specify <code>null</code> to clear this criterion. + * Returns the public key algorithm ID that matching certificates must have, + * or <code>null</code> if this criterion was not set. * - * @param key The public key. + * @return The public key algorithm ID. */ - public void setSubjectPublicKey(PublicKey key) + public String getSubjectPublicKeyAlgID() { - this.subjectKey = key; - if (key == null) - { - subjectKeySpec = null; - return; - } - try - { - KeyFactory enc = KeyFactory.getInstance("X.509"); - subjectKeySpec = (X509EncodedKeySpec) - enc.getKeySpec(key, X509EncodedKeySpec.class); - } - catch (Exception x) - { - subjectKey = null; - subjectKeySpec = null; - } + return String.valueOf(sigId); } /** - * Sets the subject public key criterion as a DER-encoded key. Specify - * <code>null</code> to clear this value. + * Match a certificate. This method will check the given certificate + * against all the enabled criteria of this selector, and will return + * <code>true</code> if the given certificate matches. * - * @param key The DER-encoded key bytes. - * @throws IOException If the argument is not a valid DER-encoded key. + * @param certificate The certificate to check. + * @return true if the certificate matches all criteria. */ - public void setSubjectPublicKey(byte[] key) throws IOException + public boolean match(Certificate certificate) { - if (key == null) + if (!(certificate instanceof X509Certificate)) + return false; + X509Certificate cert = (X509Certificate) certificate; + if (this.cert != null) { - subjectKey = null; - subjectKeySpec = null; - return; + try + { + byte[] e1 = this.cert.getEncoded(); + byte[] e2 = cert.getEncoded(); + if (!Arrays.equals(e1, e2)) + return false; + } + catch (CertificateEncodingException cee) + { + return false; + } } - try + if (serialNo != null) { - subjectKeySpec = new X509EncodedKeySpec(key); - KeyFactory enc = KeyFactory.getInstance("X.509"); - subjectKey = enc.generatePublic(subjectKeySpec); + if (!serialNo.equals(cert.getSerialNumber())) + return false; } - catch (Exception x) + if (certValid != null) { - subjectKey = null; - subjectKeySpec = null; - IOException ioe = new IOException(x.getMessage()); - ioe.initCause(x); - throw ioe; + try + { + cert.checkValidity(certValid); + } + catch (CertificateException ce) + { + return false; + } } + if (issuer != null) + { + if (!issuer.equals(cert.getIssuerX500Principal())) + return false; + } + if (subject != null) + { + if (!subject.equals(cert.getSubjectX500Principal())) + return false; + } + if (sigId != null) + { + if (!sigId.toString().equals(cert.getSigAlgOID())) + return false; + } + if (subjectKeyId != null) + { + byte[] b = cert.getExtensionValue(SUBJECT_KEY_ID); + if (!Arrays.equals(b, subjectKeyId)) + return false; + } + if (authKeyId != null) + { + byte[] b = cert.getExtensionValue(AUTH_KEY_ID); + if (!Arrays.equals(b, authKeyId)) + return false; + } + if (keyUsage != null) + { + boolean[] b = cert.getKeyUsage(); + if (!Arrays.equals(b, keyUsage)) + return false; + } + if (basicConstraints >= 0) + { + if (cert.getBasicConstraints() != basicConstraints) + return false; + } + if (keyPurposeSet != null) + { + List kp = null; + try + { + kp = cert.getExtendedKeyUsage(); + } + catch (CertificateParsingException cpe) + { + return false; + } + if (kp == null) + return false; + for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); ) + { + if (!kp.contains(it.next())) + return false; + } + } + if (altNames != null) + { + Collection<List<?>> an = null; + try + { + an = cert.getSubjectAlternativeNames(); + } + catch (CertificateParsingException cpe) + { + return false; + } + if (an == null) + return false; + int match = 0; + for (GeneralName name : altNames) + { + for (List<?> list : an) + { + try + { + Integer id = (Integer) list.get(0); + Object val = list.get(1); + GeneralName n = null; + if (val instanceof String) + n = makeName(id, (String) val); + else if (val instanceof byte[]) + { + n = new GeneralName(GeneralName.Kind.forTag(id), + (byte[]) val); + } + else + continue; + if (name.equals(n)) + match++; + } + catch (Exception e) + { + continue; + } + } + if (match == 0 || (matchAllNames && match < altNames.size())) + return false; + } + } + if (nameConstraints != null) + { + byte[] nc = cert.getExtensionValue(NAME_CONSTRAINTS_ID); + if (!Arrays.equals(nameConstraints, nc)) + return false; + } + + if (policy != null) + { + CertificatePolicies policies = null; + if (cert instanceof GnuPKIExtension) + { + policies = (CertificatePolicies) + ((GnuPKIExtension) cert).getExtension(CertificatePolicies.ID).getValue(); + } + else + { + byte[] policiesDer = + cert.getExtensionValue(CertificatePolicies.ID.toString()); + try + { + policies = new CertificatePolicies(policiesDer); + } + catch (IOException ioe) + { + // ignored + } + } + + if (policies == null) + return false; + if (!policies.getPolicies().containsAll(policy)) + return false; + } + + if (pathToNames != null) + { + NameConstraints nc = null; + if (cert instanceof GnuPKIExtension) + { + Extension e = + ((GnuPKIExtension) cert).getExtension(NameConstraints.ID); + if (e != null) + nc = (NameConstraints) e.getValue(); + } + else + { + byte[] b = cert.getExtensionValue(NameConstraints.ID.toString()); + if (b != null) + { + try + { + nc = new NameConstraints(b); + } + catch (IOException ioe) + { + } + } + } + + if (nc == null) + return false; + + int match = 0; + for (GeneralName name : pathToNames) + { + for (GeneralSubtree subtree : nc.permittedSubtrees()) + { + if (name.equals(subtree.base())) + match++; + } + } + if (match == 0 || (matchAllNames && match < pathToNames.size())) + return false; + } + + return true; } /** - * Returns the public key usage criterion, or <code>null</code> if this - * value is not set. Note that the array is cloned to prevent modification. + * Sets the authority key identifier criterion, or <code>null</code> to clear + * this criterion. Note that the byte array is cloned to prevent modification. * - * @return The public key usage. + * @param authKeyId The authority key identifier. */ - public boolean[] getKeyUsage() + public void setAuthorityKeyIdentifier(byte[] authKeyId) { - if (keyUsage != null) - return (boolean[]) keyUsage.clone(); - else - return null; + this.authKeyId = authKeyId != null ? (byte[]) authKeyId.clone() : null; } /** - * Sets the public key usage criterion. Specify <code>null</code> to clear - * this value. + * Sets the basic constraints criterion. Specify -1 to clear this parameter. * - * @param keyUsage The public key usage. + * @param basicConstraints The new basic constraints value. */ - public void setKeyUsage(boolean[] keyUsage) + public void setBasicConstraints(int basicConstraints) { - this.keyUsage = keyUsage != null ? (boolean[]) keyUsage.clone() : null; + if (basicConstraints < -1) + basicConstraints = -1; + this.basicConstraints = basicConstraints; } /** - * Returns the set of extended key purpose IDs, as an unmodifiable set - * of OID strings. Returns <code>null</code> if this criterion is not - * set. + * Sets the certificate criterion. If set, only certificates that are + * equal to the certificate passed here will be accepted. * - * @return The set of key purpose OIDs (strings). + * @param cert The certificate. */ - public Set<String> getExtendedKeyUsage() + public void setCertificate(X509Certificate cert) { - if (keyPurposeSet != null) - return Collections.unmodifiableSet(keyPurposeSet); - else - return null; + this.cert = cert; + } + + /** + * Sets the date at which certificates must be valid. Specify + * <code>null</code> to clear this criterion. + * + * @param certValid The certificate validity date. + */ + public void setCertificateValid(Date certValid) + { + this.certValid = certValid != null ? (Date) certValid.clone() : null; } /** @@ -596,7 +844,7 @@ public class X509CertSelector implements CertSelector, Cloneable this.keyPurposeSet = null; return; } - Set s = new HashSet(); + Set<String> s = new HashSet<String>(); for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); ) { Object o = it.next(); @@ -620,119 +868,77 @@ public class X509CertSelector implements CertSelector, Cloneable } /** - * Returns whether or not all specified alternative names must match. - * If false, a certificate is considered a match if <em>one</em> of the - * specified alternative names matches. - * - * @return true if all names must match. - */ - public boolean getMatchAllSubjectAltNames() - { - return matchAllNames; - } - - /** - * Sets whether or not all subject alternative names must be matched. - * If false, then a certificate will be considered a match if one - * alternative name matches. - * - * @param matchAllNames Whether or not all alternative names must be - * matched. - */ - public void setMatchAllSubjectAltNames(boolean matchAllNames) - { - this.matchAllNames = matchAllNames; - } - - /** - * Sets the subject alternative names critertion. Each element of the - * argument must be a {@link java.util.List} that contains exactly two - * elements: the first an {@link Integer}, representing the type of - * name, and the second either a {@link String} or a byte array, - * representing the name itself. + * Sets the issuer, specified as the DER encoding of the issuer's + * distinguished name. Only certificates issued by this issuer will + * be accepted. * - * @param altNames The alternative names. - * @throws IOException If any element of the argument is invalid. + * @param name The DER encoding of the issuer's distinguished name. + * @throws IOException If the given name is incorrectly formatted. */ - public void setSubjectAlternativeNames(Collection<List<?>> altNames) - throws IOException + public void setIssuer(byte[] name) throws IOException { - if (altNames == null) - { - this.altNames = null; - return; - } - List l = new ArrayList(altNames.size()); - for (Iterator it = altNames.iterator(); it.hasNext(); ) + if (name != null) { - Object o = it.next(); - if (!(o instanceof List) || ((List) o).size() != 2 || - !(((List) o).get(0) instanceof Integer) || - !(((List) o).get(1) instanceof String) || - !(((List) o).get(1) instanceof byte[])) - throw new IOException("illegal alternative name: " + o); - Integer i = (Integer) ((List) o).get(0); - if (i.intValue() < 0 || i.intValue() > 8) - throw new IOException("illegal alternative name: " + o + - ", bad id: " + i); - l.add(new ArrayList((List) o)); + try + { + issuer = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } } - this.altNames = l; + else + issuer = null; } /** - * Add a name to the subject alternative names criterion. + * Sets the issuer, specified as a string representation of the issuer's + * distinguished name. Only certificates issued by this issuer will + * be accepted. * - * @param id The type of name this is. Must be in the range [0,8]. - * @param name The name. - * @throws IOException If the id is out of range, or if the name - * is null. + * @param name The string representation of the issuer's distinguished name. + * @throws IOException If the given name is incorrectly formatted. */ - public void addSubjectAlternativeName(int id, String name) - throws IOException + public void setIssuer(String name) throws IOException { - if (id < 0 || id > 8 || name == null) - throw new IOException("illegal alternative name"); - if (altNames == null) - altNames = new LinkedList(); - ArrayList l = new ArrayList(2); - l.add(Integer.valueOf(id)); - l.add(name); - altNames.add(l); + if (name != null) + { + try + { + issuer = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } + } + else + issuer = null; } /** - * Add a name, as DER-encoded bytes, to the subject alternative names - * criterion. + * Sets the public key usage criterion. Specify <code>null</code> to clear + * this value. * - * @param id The type of name this is. + * @param keyUsage The public key usage. */ - public void addSubjectAlternativeName(int id, byte[] name) - throws IOException + public void setKeyUsage(boolean[] keyUsage) { - if (id < 0 || id > 8 || name == null) - throw new IOException("illegal alternative name"); - if (altNames == null) - altNames = new LinkedList(); - ArrayList l = new ArrayList(2); - l.add(Integer.valueOf(id)); - l.add(name); - altNames.add(l); + this.keyUsage = keyUsage != null ? (boolean[]) keyUsage.clone() : null; } /** - * Returns the name constraints criterion, or <code>null</code> if this - * value is not set. Note that the byte array is cloned to prevent - * modification. + * Sets whether or not all subject alternative names must be matched. + * If false, then a certificate will be considered a match if one + * alternative name matches. * - * @return The name constraints. + * @param matchAllNames Whether or not all alternative names must be + * matched. */ - public byte[] getNameConstraints() + public void setMatchAllSubjectAltNames(boolean matchAllNames) { - if (nameConstraints != null) - return (byte[]) nameConstraints.clone(); - else - return null; + this.matchAllNames = matchAllNames; } /** @@ -747,280 +953,302 @@ public class X509CertSelector implements CertSelector, Cloneable public void setNameConstraints(byte[] nameConstraints) throws IOException { - // FIXME check if the argument is valid. + // Check if the input is well-formed... + new NameConstraints(nameConstraints); + + // But we just compare raw byte arrays. this.nameConstraints = nameConstraints != null ? (byte[]) nameConstraints.clone() : null; } + + /** + * Sets the pathToNames criterion. The argument is a collection of + * pairs, the first element of which is an {@link Integer} giving + * the ID of the name, and the second element is either a {@link String} + * or a byte array. + * + * See {@link #addPathToName(int, byte[])} and {@link #addPathToName(int, String)} + * for how these arguments are handled. + * + * @param names The names. + * @throws IOException If any argument is malformed. + */ + public void setPathToNames(Collection<List<?>> names) throws IOException + { + if (names == null || names.size() == 0) + { + pathToNames = null; + } + else + { + pathToNames = new ArrayList<GeneralName>(names.size()); + for (List<?> name : names) + { + Integer id = (Integer) name.get(0); + Object name2 = name.get(1); + if (name2 instanceof String) + addPathToName(id, (String) name2); + else if (name2 instanceof byte[]) + addPathToName(id, (byte[]) name2); + else + throw new IOException("invalid name type: " + + name2.getClass().getName()); + } + } + } /** - * Returns the basic constraints criterion, or -1 if this value is not set. + * Sets the certificate policy to match, or null if this criterion should + * not be checked. Each element if the set must be a dotted-decimal form + * of certificate policy object identifier. * - * @return The basic constraints. + * @param policy The policy to match. + * @throws IOException If some element of the policy is not a valid + * policy extenison OID. */ - public int getBasicConstraints() + public void setPolicy(Set<String> policy) throws IOException { - return basicConstraints; + if (policy != null) + { + HashSet<OID> p = new HashSet<OID>(policy.size()); + for (String s : policy) + { + try + { + OID oid = new OID(s); + int[] i = oid.getIDs(); + if (!checkOid(i)) + throw new IOException("invalid OID"); + p.add(oid); + } + catch (IOException ioe) + { + throw ioe; + } + catch (Exception x) + { + IOException ioe = new IOException("invalid OID"); + ioe.initCause(x); + throw ioe; + } + } + this.policy = p; + } + else + this.policy = null; } /** - * Sets the basic constraints criterion. Specify -1 to clear this parameter. + * This method, and its related X.509 certificate extension — the + * private key usage period — is not supported under the Internet + * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this + * method is not supported either. * - * @param basicConstraints The new basic constraints value. + * <p>Do not use this method. It is not deprecated, as it is not deprecated + * in the Java standard, but it is basically a no-operation. + * + * @param UNUSED Is silently ignored. */ - public void setBasicConstraints(int basicConstraints) + public void setPrivateKeyValid(Date UNUSED) { - if (basicConstraints < -1) - basicConstraints = -1; - this.basicConstraints = basicConstraints; } - // The last two criteria not yet implemented are certificate policies - // and path-to-names. Both of these are somewhat advanced extensions - // (you could probably count the applications that actually use them - // on one hand), and they both have no support in the X509Certificate - // class. - // - // Not having support in X509Certificate is not always a problem; for - // example, we can compare DER-encoded values as byte arrays for some - // extensions. We can't, however, compare them if they are specified - // in a set (as policies are). We need to parse the actual value in the - // certificate, and check it against the specified set. - - // FIXME -// public void setPolicy(Set<String> policy) throws IOException -// { -// if (policy != null) -// { -// for (Iterator it = policy.iterator(); it.hasNext(); ) -// try -// { -// OID oid = new OID((String) it.next()); -// int[] i = oid.getIDs(); -// if (!checkOid(i)) -// throw new IOException("invalid OID"); -// } -// catch (Exception x) -// { -// throw new IOException("invalid OID"); -// } -// } -// this.policy = policy != null ? new HashSet(policy) : null; -// } - - // FIXME -// public void setPathToNames(Collection<List<?>> names) throws IOException -// { -// if (names == null) -// { -// this.names = null; -// return; -// } -// for (Iterator it = names.iterator(); it.hasNext(); ) -// { -// try -// { -// List l = (List) it.next(); -// if (l.get(1) instanceof String) -// addPathToName(((Integer)l.get(0)).intValue(), (String)l.get(1)); -// else -// addPathToName(((Integer)l.get(0)).intValue(), (byte[])l.get(1)); -// } -// catch (Exception x) -// { -// this.names = null; -// throw new IOException("invalid names"); -// } -// } -// } - - // FIXME -// public void addPathToName(int id, String name) throws IOException -// { -// } - - // FIXME -// public void addPathToName(int id, byte[] name) throws IOException -// { -// } - - // FIXME -// public Collection<List<?>> getSubjectAlternativeNames() -// { -// return null; -// } - - // FIXME -// public Set<String> getPolicy() -// { -// return null; -// } - - // FIXME -// public Collection<List<?>> getPathToNames() -// { -// return null; -// } + /** + * Sets the serial number of the desired certificate. Only certificates that + * contain this serial number are accepted. + * + * @param serialNo The serial number. + */ + public void setSerialNumber(BigInteger serialNo) + { + this.serialNo = serialNo; + } /** - * Match a certificate. This method will check the given certificate - * against all the enabled criteria of this selector, and will return - * <code>true</code> if the given certificate matches. + * Sets the subject, specified as the DER encoding of the subject's + * distinguished name. Only certificates with the given subject will + * be accepted. * - * @param certificate The certificate to check. - * @return true if the certificate matches all criteria. + * @param name The DER encoding of the subject's distinguished name. + * @throws IOException If the given name is incorrectly formatted. */ - public boolean match(Certificate certificate) + public void setSubject(byte[] name) throws IOException { - if (!(certificate instanceof X509Certificate)) - return false; - X509Certificate cert = (X509Certificate) certificate; - if (this.cert != null) + if (name != null) { try { - byte[] e1 = this.cert.getEncoded(); - byte[] e2 = cert.getEncoded(); - if (!Arrays.equals(e1, e2)) - return false; + subject = new X500Principal(name); } - catch (CertificateEncodingException cee) + catch (IllegalArgumentException iae) { - return false; + throw new IOException(iae.getMessage()); } } - if (serialNo != null) - { - if (!serialNo.equals(cert.getSerialNumber())) - return false; - } - if (certValid != null) + else + subject = null; + } + + /** + * Sets the subject, specified as a string representation of the + * subject's distinguished name. Only certificates with the given + * subject will be accepted. + * + * @param name The string representation of the subject's distinguished name. + * @throws IOException If the given name is incorrectly formatted. + */ + public void setSubject(String name) throws IOException + { + if (name != null) { try { - cert.checkValidity(certValid); + subject = new X500Principal(name); } - catch (CertificateException ce) + catch (IllegalArgumentException iae) { - return false; + throw new IOException(iae.getMessage()); } } - if (issuer != null) + else + subject = null; + } + + /** + * Sets the subject alternative names critertion. Each element of the + * argument must be a {@link java.util.List} that contains exactly two + * elements: the first an {@link Integer}, representing the type of + * name, and the second either a {@link String} or a byte array, + * representing the name itself. + * + * @param altNames The alternative names. + * @throws IOException If any element of the argument is invalid. + */ + public void setSubjectAlternativeNames(Collection<List<?>> altNames) + throws IOException + { + if (altNames == null || altNames.isEmpty()) { - if (!issuer.equals(cert.getIssuerX500Principal())) - return false; + this.altNames = null; + return; } - if (subject != null) + List<GeneralName> l = new ArrayList<GeneralName>(altNames.size()); + for (List<?> list : altNames) { - if (!subject.equals(cert.getSubjectX500Principal())) - return false; + Integer id = (Integer) list.get(0); + Object value = list.get(1); + GeneralName name = null; + if (value instanceof String) + name = makeName(id, (String) value); + else if (value instanceof byte[]) + name = new GeneralName(GeneralName.Kind.forTag(id), (byte[]) value); + else + throw new IOException("invalid name type: " + value.getClass().getName()); + l.add(name); } - if (sigId != null) + this.altNames = l; + } + + /** + * Sets the subject key identifier criterion, or <code>null</code> to clear + * this criterion. Note that the byte array is cloned to prevent modification. + * + * @param subjectKeyId The subject key identifier. + */ + public void setSubjectKeyIdentifier(byte[] subjectKeyId) + { + this.subjectKeyId = subjectKeyId != null ? (byte[]) subjectKeyId.clone() : + null; + } + + /** + * Sets the subject public key criterion as a DER-encoded key. Specify + * <code>null</code> to clear this value. + * + * @param key The DER-encoded key bytes. + * @throws IOException If the argument is not a valid DER-encoded key. + */ + public void setSubjectPublicKey(byte[] key) throws IOException + { + if (key == null) { - if (!sigId.toString().equals(cert.getSigAlgOID())) - return false; + subjectKey = null; + subjectKeySpec = null; + return; } - if (subjectKeyId != null) + try { - byte[] b = cert.getExtensionValue(SUBJECT_KEY_ID); - if (!Arrays.equals(b, subjectKeyId)) - return false; + subjectKeySpec = new X509EncodedKeySpec(key); + KeyFactory enc = KeyFactory.getInstance("X.509"); + subjectKey = enc.generatePublic(subjectKeySpec); } - if (authKeyId != null) + catch (Exception x) { - byte[] b = cert.getExtensionValue(AUTH_KEY_ID); - if (!Arrays.equals(b, authKeyId)) - return false; + subjectKey = null; + subjectKeySpec = null; + IOException ioe = new IOException(x.getMessage()); + ioe.initCause(x); + throw ioe; } - if (keyUsage != null) + } + + /** + * Sets the subject public key criterion as an opaque representation. + * Specify <code>null</code> to clear this criterion. + * + * @param key The public key. + */ + public void setSubjectPublicKey(PublicKey key) + { + this.subjectKey = key; + if (key == null) { - boolean[] b = cert.getKeyUsage(); - if (!Arrays.equals(b, keyUsage)) - return false; + subjectKeySpec = null; + return; } - if (basicConstraints >= 0) + try { - if (cert.getBasicConstraints() != basicConstraints) - return false; + KeyFactory enc = KeyFactory.getInstance("X.509"); + subjectKeySpec = (X509EncodedKeySpec) + enc.getKeySpec(key, X509EncodedKeySpec.class); } - if (keyPurposeSet != null) + catch (Exception x) { - List kp = null; - try - { - kp = cert.getExtendedKeyUsage(); - } - catch (CertificateParsingException cpe) - { - return false; - } - if (kp == null) - return false; - for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); ) - { - if (!kp.contains(it.next())) - return false; - } + subjectKey = null; + subjectKeySpec = null; } - if (altNames != null) + } + + /** + * Sets the public key algorithm ID that matching certificates must have. + * Specify <code>null</code> to clear this criterion. + * + * @param sigId The public key ID. + * @throws IOException If the specified ID is not a valid object identifier. + */ + public void setSubjectPublicKeyAlgID(String sigId) throws IOException + { + if (sigId != null) { - Collection an = null; try { - an = cert.getSubjectAlternativeNames(); - } - catch (CertificateParsingException cpe) - { - return false; + OID oid = new OID(sigId); + int[] comp = oid.getIDs(); + if (!checkOid(comp)) + throw new IOException("malformed OID: " + sigId); + this.sigId = oid; } - if (an == null) - return false; - int match = 0; - for (Iterator it = altNames.iterator(); it.hasNext(); ) + catch (IllegalArgumentException iae) { - List l = (List) it.next(); - Integer id = (Integer) l.get(0); - String s = null; - byte[] b = null; - if (l.get(1) instanceof String) - s = (String) l.get(1); - else if (l.get(1) instanceof byte[]) - b = (byte[]) l.get(1); - else - return false; - for (Iterator it2 = an.iterator(); it2.hasNext(); ) - { - Object o = it2.next(); - if (!(o instanceof List)) - continue; - List l2 = (List) o; - if (l2.size() != 2) - continue; - if (!id.equals(l2.get(0))) - continue; - if (s != null && (l2.get(1) instanceof String) && - s.equals(l2.get(1))) - match++; - else if (b != null && (l2.get(1) instanceof byte[]) && - Arrays.equals(b, (byte[]) l2.get(1))) - match++; - } - if (match == 0 || (matchAllNames && match != altNames.size())) - return false; + IOException ioe = new IOException("malformed OID: " + sigId); + ioe.initCause(iae); + throw ioe; } } - if (nameConstraints != null) - { - byte[] nc = cert.getExtensionValue(NAME_CONSTRAINTS_ID); - if (!Arrays.equals(nameConstraints, nc)) - return false; - } - - // FIXME check policies. - // FIXME check path-to-names. - - return true; + else + this.sigId = null; } - + public String toString() { StringBuffer str = new StringBuffer(X509CertSelector.class.getName()); @@ -1080,28 +1308,11 @@ public class X509CertSelector implements CertSelector, Cloneable str.append(" alternative names = ").append(altNames).append(eol); if (nameConstraints != null) str.append(" name constraints = <blob of data>").append(eol); + if (policy != null) + str.append(" policy = ").append(policy).append(eol); + if (pathToNames != null) + str.append(" pathToNames = ").append(pathToNames).append(eol); str.append("}").append(nl); return str.toString(); } - - public Object clone() - { - try - { - return super.clone(); - } - catch (CloneNotSupportedException shouldNotHappen) - { - throw new Error(shouldNotHappen); - } - } - - // Own methods. - // ------------------------------------------------------------------------- - - private static boolean checkOid(int[] oid) - { - return (oid != null && oid.length > 2 && - (oid[0] >= 0 && oid[0] <= 2) && (oid[1] >= 0 && oid[1] <= 39)); - } } diff --git a/libjava/classpath/java/security/cert/X509Certificate.java b/libjava/classpath/java/security/cert/X509Certificate.java index bc1b5c2351c..b398e093e6a 100644 --- a/libjava/classpath/java/security/cert/X509Certificate.java +++ b/libjava/classpath/java/security/cert/X509Certificate.java @@ -1,5 +1,5 @@ /* X509Certificate.java --- X.509 Certificate class - Copyright (C) 1999,2003 Free Software Foundation, Inc. + Copyright (C) 1999,2003, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -137,7 +137,7 @@ import java.util.List; * @author Casey Marshall (rsdio@metastatic.org) */ public abstract class X509Certificate - extends java.security.cert.Certificate // XXX workaround for gcj bug #17845 + extends Certificate implements X509Extension { private static final long serialVersionUID = -2491127588187038216L; diff --git a/libjava/classpath/java/text/AttributedStringIterator.java b/libjava/classpath/java/text/AttributedStringIterator.java index 422876c0948..2f970feba43 100644 --- a/libjava/classpath/java/text/AttributedStringIterator.java +++ b/libjava/classpath/java/text/AttributedStringIterator.java @@ -213,9 +213,7 @@ class AttributedStringIterator implements AttributedCharacterIterator Iterator iterator = attributeSet.iterator(); while (iterator.hasNext()) { - // Qualified name is a workaround for a gcj 4.0 bug. - AttributedCharacterIterator.Attribute attributeKey - = (AttributedCharacterIterator.Attribute) iterator.next(); + Attribute attributeKey = (Attribute) iterator.next(); Object v1 = runValues.get(attributeKey); Object v2 = getAttribute(attributeKey, limit + 1); boolean changed = false; @@ -298,9 +296,7 @@ class AttributedStringIterator implements AttributedCharacterIterator Iterator iterator = attributeSet.iterator(); while (iterator.hasNext()) { - // Qualified name is a workaround for a gcj 4.0 bug. - AttributedCharacterIterator.Attribute attributeKey - = (AttributedCharacterIterator.Attribute) iterator.next(); + Attribute attributeKey = (Attribute) iterator.next(); Object v1 = runValues.get(attributeKey); Object v2 = getAttribute(attributeKey, prev); boolean changed = false; diff --git a/libjava/classpath/java/text/BreakIterator.java b/libjava/classpath/java/text/BreakIterator.java index 7ba116870de..30e5f2b9491 100644 --- a/libjava/classpath/java/text/BreakIterator.java +++ b/libjava/classpath/java/text/BreakIterator.java @@ -1,5 +1,6 @@ /* BreakIterator.java -- Breaks text into elements - Copyright (C) 1998, 1999, 2001, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2001, 2004, 2005, 2007 + Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,9 +39,19 @@ exception statement from your version. */ package java.text; +import gnu.java.locale.LocaleHelper; + +import gnu.java.text.CharacterBreakIterator; +import gnu.java.text.LineBreakIterator; +import gnu.java.text.SentenceBreakIterator; +import gnu.java.text.WordBreakIterator; + +import java.text.spi.BreakIteratorProvider; + import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; +import java.util.ServiceLoader; /** * This class iterates over text elements such as words, lines, sentences, @@ -179,19 +190,34 @@ public abstract class BreakIterator implements Cloneable /** * This method returns an instance of <code>BreakIterator</code> that will - * iterate over characters as defined in the specified locale. If the - * desired locale is not available, the default locale is used. + * iterate over characters as defined in the specified locale. * * @param locale The desired locale. * - * @return A <code>BreakIterator</code> instance for the default locale. + * @return A <code>BreakIterator</code> instance for the specified locale. */ public static BreakIterator getCharacterInstance (Locale locale) { - BreakIterator r = getInstance ("CharacterIterator", locale); - if (r == null) - r = new gnu.java.text.CharacterBreakIterator (); - return r; + BreakIterator r = getInstance("CharacterIterator", locale); + if (r != null) + return r; + for (BreakIteratorProvider p : + ServiceLoader.load(BreakIteratorProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + BreakIterator bi = p.getCharacterInstance(locale); + if (bi != null) + return bi; + break; + } + } + } + if (locale.equals(Locale.ROOT)) + return new CharacterBreakIterator(); + return getCharacterInstance(LocaleHelper.getFallbackLocale(locale)); } /** @@ -207,8 +233,7 @@ public abstract class BreakIterator implements Cloneable /** * This method returns an instance of <code>BreakIterator</code> that will - * iterate over line breaks as defined in the specified locale. If the - * desired locale is not available, the default locale is used. + * iterate over line breaks as defined in the specified locale. * * @param locale The desired locale. * @@ -217,9 +242,25 @@ public abstract class BreakIterator implements Cloneable public static BreakIterator getLineInstance (Locale locale) { BreakIterator r = getInstance ("LineIterator", locale); - if (r == null) - r = new gnu.java.text.LineBreakIterator (); - return r; + if (r != null) + return r; + for (BreakIteratorProvider p : + ServiceLoader.load(BreakIteratorProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + BreakIterator bi = p.getLineInstance(locale); + if (bi != null) + return bi; + break; + } + } + } + if (locale.equals(Locale.ROOT)) + return new LineBreakIterator(); + return getLineInstance(LocaleHelper.getFallbackLocale(locale)); } /** @@ -235,8 +276,7 @@ public abstract class BreakIterator implements Cloneable /** * This method returns an instance of <code>BreakIterator</code> that will - * iterate over sentences as defined in the specified locale. If the - * desired locale is not available, the default locale is used. + * iterate over sentences as defined in the specified locale. * * @param locale The desired locale. * @@ -245,9 +285,25 @@ public abstract class BreakIterator implements Cloneable public static BreakIterator getSentenceInstance (Locale locale) { BreakIterator r = getInstance ("SentenceIterator", locale); - if (r == null) - r = new gnu.java.text.SentenceBreakIterator (); - return r; + if (r != null) + return r; + for (BreakIteratorProvider p : + ServiceLoader.load(BreakIteratorProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + BreakIterator bi = p.getSentenceInstance(locale); + if (bi != null) + return bi; + break; + } + } + } + if (locale.equals(Locale.ROOT)) + return new SentenceBreakIterator(); + return getSentenceInstance(LocaleHelper.getFallbackLocale(locale)); } /** @@ -271,8 +327,7 @@ public abstract class BreakIterator implements Cloneable /** * This method returns an instance of <code>BreakIterator</code> that will - * iterate over words as defined in the specified locale. If the - * desired locale is not available, the default locale is used. + * iterate over words as defined in the specified locale. * * @param locale The desired locale. * @@ -281,9 +336,25 @@ public abstract class BreakIterator implements Cloneable public static BreakIterator getWordInstance (Locale locale) { BreakIterator r = getInstance ("WordIterator", locale); - if (r == null) - r = new gnu.java.text.WordBreakIterator (); - return r; + if (r != null) + return r; + for (BreakIteratorProvider p : + ServiceLoader.load(BreakIteratorProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + BreakIterator bi = p.getWordInstance(locale); + if (bi != null) + return bi; + break; + } + } + } + if (locale.equals(Locale.ROOT)) + return new WordBreakIterator(); + return getWordInstance(LocaleHelper.getFallbackLocale(locale)); } /** diff --git a/libjava/classpath/java/text/Collator.java b/libjava/classpath/java/text/Collator.java index 95236132440..16ee6b1241b 100644 --- a/libjava/classpath/java/text/Collator.java +++ b/libjava/classpath/java/text/Collator.java @@ -40,10 +40,13 @@ package java.text; import gnu.java.locale.LocaleHelper; +import java.text.spi.CollatorProvider; + import java.util.Comparator; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; +import java.util.ServiceLoader; /** * This class is the abstract superclass of classes which perform @@ -285,7 +288,8 @@ public abstract class Collator implements Comparator<Object>, Cloneable /** * This method returns an instance of <code>Collator</code> for the * specified locale. If no <code>Collator</code> exists for the desired - * locale, a <code>Collator</code> for the default locale will be returned. + * locale, the fallback procedure described in + * {@link java.util.spi.LocaleServiceProvider} is invoked. * * @param loc The desired locale to load a <code>Collator</code> for. * @@ -293,27 +297,51 @@ public abstract class Collator implements Comparator<Object>, Cloneable */ public static Collator getInstance (Locale loc) { - ResourceBundle res; String pattern; try { - res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", - loc, ClassLoader.getSystemClassLoader()); - pattern = res.getString("collation_rules"); + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + return new RuleBasedCollator(res.getString("collation_rules")); } catch (MissingResourceException x) { - pattern = "<0<1<2<3<4<5<6<7<8<9<A,a<b,B<c,C<d,D<e,E<f,F<g,G<h,H<i,I<j,J<k,K" + - "<l,L<m,M<n,N<o,O<p,P<q,Q<r,R<s,S<t,T<u,U<v,V<w,W<x,X<y,Y<z,Z"; - } - try - { - return new RuleBasedCollator (pattern); + /* This means runtime support for the locale + * is not available, so we check providers. */ } catch (ParseException x) { throw (InternalError)new InternalError().initCause(x); } + for (CollatorProvider p : ServiceLoader.load(CollatorProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + Collator c = p.getInstance(loc); + if (c != null) + return c; + break; + } + } + } + if (loc.equals(Locale.ROOT)) + { + try + { + return new RuleBasedCollator("<0<1<2<3<4<5<6<7<8<9<A,a<b,B<c," + + "C<d,D<e,E<f,F<g,G<h,H<i,I<j,J<k,K" + + "<l,L<m,M<n,N<o,O<p,P<q,Q<r,R<s,S<t,"+ + "T<u,U<v,V<w,W<x,X<y,Y<z,Z"); + } + catch (ParseException x) + { + throw (InternalError)new InternalError().initCause(x); + } + } + return getInstance(LocaleHelper.getFallbackLocale(loc)); } /** diff --git a/libjava/classpath/java/text/DateFormat.java b/libjava/classpath/java/text/DateFormat.java index 73aa62d9805..53b757e88b6 100644 --- a/libjava/classpath/java/text/DateFormat.java +++ b/libjava/classpath/java/text/DateFormat.java @@ -39,12 +39,17 @@ exception statement from your version. */ package java.text; +import gnu.java.locale.LocaleHelper; + +import java.text.spi.DateFormatProvider; + import java.io.InvalidObjectException; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; +import java.util.ServiceLoader; import java.util.TimeZone; /** @@ -550,17 +555,14 @@ public abstract class DateFormat extends Format implements Cloneable private static DateFormat computeInstance (int dateStyle, int timeStyle, Locale loc, boolean use_date, boolean use_time) + throws MissingResourceException { - ResourceBundle res; - try - { - res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", - loc, ClassLoader.getSystemClassLoader()); - } - catch (MissingResourceException x) - { - res = null; - } + if (loc.equals(Locale.ROOT)) + return computeDefault(dateStyle,timeStyle,use_date,use_time); + + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); String pattern = null; if (use_date) @@ -642,6 +644,59 @@ public abstract class DateFormat extends Format implements Cloneable return new SimpleDateFormat (pattern, loc); } + private static DateFormat computeDefault (int dateStyle, int timeStyle, + boolean use_date, boolean use_time) + { + String pattern = null; + if (use_date) + { + switch (dateStyle) + { + case FULL: + pattern = "EEEE MMMM d, yyyy G"; + break; + case LONG: + pattern = "MMMM d, yyyy"; + break; + case MEDIUM: + pattern = "d-MMM-yy"; + break; + case SHORT: + pattern = "M/d/yy"; + default: + throw new IllegalArgumentException (); + } + } + + if (use_time) + { + if (pattern == null) + pattern = ""; + else + pattern += " "; + + switch (timeStyle) + { + case FULL: + pattern += "h:mm:ss;S 'o''clock' a z"; + break; + case LONG: + pattern += "h:mm:ss a z"; + break; + case MEDIUM: + pattern += "h:mm:ss a"; + break; + case SHORT: + pattern += "h:mm a"; + break; + default: + throw new IllegalArgumentException (); + } + } + + return new SimpleDateFormat (pattern, Locale.ROOT); + } + /** * This method returns an instance of <code>DateFormat</code> that will * format using the default formatting style for dates. @@ -678,7 +733,29 @@ public abstract class DateFormat extends Format implements Cloneable */ public static final DateFormat getDateInstance (int style, Locale loc) { - return computeInstance (style, loc, true, false); + try + { + return computeInstance (style, loc, true, false); + } + catch (MissingResourceException e) + { + for (DateFormatProvider p : + ServiceLoader.load(DateFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + DateFormat df = p.getDateInstance(style, loc); + if (df != null) + return df; + break; + } + } + } + return getDateInstance(style, + LocaleHelper.getFallbackLocale(loc)); + } } /** @@ -717,7 +794,30 @@ public abstract class DateFormat extends Format implements Cloneable int timeStyle, Locale loc) { - return computeInstance (dateStyle, timeStyle, loc, true, true); + try + { + return computeInstance (dateStyle, timeStyle, loc, true, true); + } + catch (MissingResourceException e) + { + for (DateFormatProvider p : + ServiceLoader.load(DateFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + DateFormat df = p.getDateTimeInstance(dateStyle, + timeStyle, loc); + if (df != null) + return df; + break; + } + } + } + return getDateTimeInstance(dateStyle, timeStyle, + LocaleHelper.getFallbackLocale(loc)); + } } /** @@ -779,7 +879,29 @@ public abstract class DateFormat extends Format implements Cloneable */ public static final DateFormat getTimeInstance (int style, Locale loc) { - return computeInstance (style, loc, false, true); + try + { + return computeInstance (style, loc, false, true); + } + catch (MissingResourceException e) + { + for (DateFormatProvider p : + ServiceLoader.load(DateFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + DateFormat df = p.getTimeInstance(style, loc); + if (df != null) + return df; + break; + } + } + } + return getTimeInstance(style, + LocaleHelper.getFallbackLocale(loc)); + } } /** diff --git a/libjava/classpath/java/text/DateFormatSymbols.java b/libjava/classpath/java/text/DateFormatSymbols.java index bffd31fb6a7..406376a0ff5 100644 --- a/libjava/classpath/java/text/DateFormatSymbols.java +++ b/libjava/classpath/java/text/DateFormatSymbols.java @@ -1,5 +1,5 @@ /* DateFormatSymbols.java -- Format over a range of numbers - Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,14 +38,26 @@ exception statement from your version. */ package java.text; +import gnu.java.locale.LocaleHelper; + +import java.text.spi.DateFormatSymbolsProvider; + +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; +import java.util.ServiceLoader; +import java.util.TimeZone; + +import java.util.spi.TimeZoneNameProvider; /** * This class acts as container for locale specific date/time formatting * information such as the days of the week and the months of the year. + * * @author Per Bothner (bothner@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) * @date October 24, 1998. */ /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3. @@ -60,6 +72,15 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable String[] shortMonths; String[] shortWeekdays; String[] weekdays; + + /** + * The timezone strings supplied by the runtime. + */ + private String[][] runtimeZoneStrings; + + /** + * Custom timezone strings supplied by {@link #setZoneStrings()}. + */ private String[][] zoneStrings; private static final long serialVersionUID = -5987973545549424702L; @@ -83,22 +104,52 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable return res.getString(name).split("\u00ae"); } - private String[][] getZoneStrings(ResourceBundle res) + private String[][] getZoneStrings(ResourceBundle res, Locale locale) { + List<String[]> allZones = new ArrayList<String[]>(); try { int index = 0; String data = res.getString("zoneStrings"); String[] zones = data.split("\u00a9"); - String[][] array = new String[zones.length][]; for (int a = 0; a < zones.length; ++a) - array[a] = zones[a].split("\u00ae"); - return array; + allZones.add(zones[a].split("\u00ae")); } catch (MissingResourceException e) { - return new String[0][]; + /* This means runtime support for the locale + * is not available, so we just include providers. */ } + for (TimeZoneNameProvider p : + ServiceLoader.load(TimeZoneNameProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + for (String id : TimeZone.getAvailableIDs()) + { + String[] z = new String[5]; + z[0] = id; + z[1] = p.getDisplayName(id, false, + TimeZone.LONG, + locale); + z[2] = p.getDisplayName(id, false, + TimeZone.SHORT, + locale); + z[3] = p.getDisplayName(id, true, + TimeZone.LONG, + locale); + z[4] = p.getDisplayName(id, true, + TimeZone.SHORT, + locale); + allZones.add(z); + } + break; + } + } + } + return allZones.toArray(new String[allZones.size()][]); } private String[] formatsForKey(ResourceBundle res, String key) @@ -114,11 +165,18 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable /** * This method initializes a new instance of <code>DateFormatSymbols</code> * by loading the date format information for the specified locale. + * This constructor only obtains instances using the runtime's resources; + * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances, + * call {@link #getInstance(java.util.Locale)} instead. * * @param locale The locale for which date formatting symbols should * be loaded. + * @throws MissingResourceException if the resources for the specified + * locale could not be found or loaded. + * @see #getInstance(java.util.Locale) */ - public DateFormatSymbols (Locale locale) throws MissingResourceException + public DateFormatSymbols (Locale locale) + throws MissingResourceException { ResourceBundle res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", locale, @@ -131,16 +189,23 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable shortMonths = getStringArray(res, "shortMonths"); shortWeekdays = getStringArray(res, "shortWeekdays"); weekdays = getStringArray(res, "weekdays"); - zoneStrings = getZoneStrings(res); + runtimeZoneStrings = getZoneStrings(res, locale); dateFormats = formatsForKey(res, "DateFormat"); timeFormats = formatsForKey(res, "TimeFormat"); } /** * This method loads the format symbol information for the default - * locale. + * locale. This constructor only obtains instances using the runtime's resources; + * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances, + * call {@link #getInstance()} instead. + * + * @throws MissingResourceException if the resources for the default + * locale could not be found or loaded. + * @see #getInstance() */ - public DateFormatSymbols () throws MissingResourceException + public DateFormatSymbols() + throws MissingResourceException { this (Locale.getDefault()); } @@ -274,12 +339,21 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable * <li>3 - The long name of the time zone (daylight savings time).</li> * <li>4 - the short name of the time zone (daylight savings time).</li> * </ul> + * <p> + * If {@link #setZoneStrings(String[][])} has been called, then the value + * passed to this will be returned. Otherwise the returned array contains + * zone names provided by the runtime environment and any + * {@link java.util.spi.TimeZoneProvider} instances. + * </p> * * @return The list of time zone display strings. + * @see #setZoneStrings(String[][]) */ - public String[] [] getZoneStrings () + public String[][] getZoneStrings() { - return zoneStrings; + if (zoneStrings != null) + return zoneStrings; + return runtimeZoneStrings; } /** @@ -537,4 +611,65 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable ^ hashCode(weekdays) ^ hashCode(zoneStrings)); } + + /** + * Returns a {@link DateFormatSymbols} instance for the + * default locale obtained from either the runtime itself + * or one of the installed + * {@link java.text.spi.DateFormatSymbolsProvider} instances. + * This is equivalent to calling + * <code>getInstance(Locale.getDefault())</code>. + * + * @return a {@link DateFormatSymbols} instance for the default + * locale. + * @since 1.6 + */ + public static final DateFormatSymbols getInstance() + { + return getInstance(Locale.getDefault()); + } + + /** + * Returns a {@link DateFormatSymbols} instance for the + * specified locale obtained from either the runtime itself + * or one of the installed + * {@link java.text.spi.DateFormatSymbolsProvider} instances. + * + * @param locale the locale for which an instance should be + * returned. + * @return a {@link DateFormatSymbols} instance for the specified + * locale. + * @throws NullPointerException if <code>locale</code> is + * <code>null</code>. + * @since 1.6 + */ + public static final DateFormatSymbols getInstance(Locale locale) + { + try + { + DateFormatSymbols syms = new DateFormatSymbols(locale); + return syms; + } + catch (MissingResourceException e) + { + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + for (DateFormatSymbolsProvider p : + ServiceLoader.load(DateFormatSymbolsProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + DateFormatSymbols syms = p.getInstance(locale); + if (syms != null) + return syms; + break; + } + } + } + return getInstance(LocaleHelper.getFallbackLocale(locale)); + } + } diff --git a/libjava/classpath/java/text/DecimalFormat.java b/libjava/classpath/java/text/DecimalFormat.java index 529e571331f..7febdeb49c4 100644 --- a/libjava/classpath/java/text/DecimalFormat.java +++ b/libjava/classpath/java/text/DecimalFormat.java @@ -439,8 +439,8 @@ public class DecimalFormat extends NumberFormat FieldPosition pos = (FieldPosition) attributes.get(i); Format.Field attribute = pos.getFieldAttribute(); - as.addAttribute(attribute, attribute, pos.getBeginIndex(), pos - .getEndIndex()); + as.addAttribute(attribute, attribute, pos.getBeginIndex(), + pos.getEndIndex()); } // return the CharacterIterator from AttributedString @@ -659,6 +659,7 @@ public class DecimalFormat extends NumberFormat // correct the size of the end parsing flag int len = str.length(); if (len < stop) stop = len; + char groupingSeparator = symbols.getGroupingSeparator(); int i = start; while (i < stop) @@ -672,6 +673,7 @@ public class DecimalFormat extends NumberFormat } else if (this.parseIntegerOnly) { + i--; break; } else if (ch == decimalSeparator) @@ -688,8 +690,19 @@ public class DecimalFormat extends NumberFormat if (inExponent) number.append(ch); else - break; + { + i--; + break; + } } + else + { + if (!groupingUsed || ch != groupingSeparator) + { + i--; + break; + } + } } // 2nd special case: infinity @@ -723,25 +736,25 @@ public class DecimalFormat extends NumberFormat // now we have to check the suffix, done here after number parsing // or the index will not be updated correctly... - boolean isNegativeSuffix = str.endsWith(this.negativeSuffix); - boolean isPositiveSuffix = str.endsWith(this.positiveSuffix); + boolean hasNegativeSuffix = str.endsWith(this.negativeSuffix); + boolean hasPositiveSuffix = str.endsWith(this.positiveSuffix); boolean positiveEqualsNegative = negativeSuffix.equals(positiveSuffix); positiveLen = positiveSuffix.length(); negativeLen = negativeSuffix.length(); - if (isNegative && !isNegativeSuffix) + if (isNegative && !hasNegativeSuffix) { pos.setErrorIndex(i); return null; } - else if (isNegativeSuffix && + else if (hasNegativeSuffix && !positiveEqualsNegative && (negativeLen > positiveLen)) { isNegative = true; } - else if (!isPositiveSuffix) + else if (!hasPositiveSuffix) { pos.setErrorIndex(i); return null; @@ -749,7 +762,7 @@ public class DecimalFormat extends NumberFormat if (isNegative) number.insert(0, '-'); - pos.setIndex(i - 1); + pos.setIndex(i); // now we handle the return type BigDecimal bigDecimal = new BigDecimal(number.toString()); diff --git a/libjava/classpath/java/text/DecimalFormatSymbols.java b/libjava/classpath/java/text/DecimalFormatSymbols.java index 29d2d7ed337..f87ebbf138f 100644 --- a/libjava/classpath/java/text/DecimalFormatSymbols.java +++ b/libjava/classpath/java/text/DecimalFormatSymbols.java @@ -1,5 +1,5 @@ /* DecimalFormatSymbols.java -- Format symbols used by DecimalFormat - Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2004, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,13 +38,19 @@ exception statement from your version. */ package java.text; +import gnu.java.locale.LocaleHelper; + import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; + +import java.text.spi.DecimalFormatSymbolsProvider; + import java.util.Currency; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; +import java.util.ServiceLoader; /** * This class is a container for the symbols used by @@ -80,6 +86,11 @@ public class DecimalFormatSymbols implements Cloneable, Serializable /** * This method initializes a new instance of * <code>DecimalFormatSymbols</code> for the default locale. + * This constructor only obtains instances using the runtime's resources; + * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances, + * call {@link #getInstance()} instead. + * + * @see #getInstance() */ public DecimalFormatSymbols () { @@ -137,18 +148,19 @@ public class DecimalFormatSymbols implements Cloneable, Serializable * international currency symbol will be set to the strings "?" * and "XXX" respectively. This generally happens with language * locales (those with no specified country), such as - * <code>Locale.ENGLISH</code>. + * <code>Locale.ENGLISH</code>. This constructor only obtains + * instances using the runtime's resources; to also include + * {@link java.text.spi.DecimalFormatSymbolsProvider} instances, + * call {@link #getInstance(java.util.Locale)} instead. * * @param loc The local to load symbols for. * @throws NullPointerException if the locale is null. + * @see #getInstance(java.util.Locale) */ public DecimalFormatSymbols (Locale loc) { ResourceBundle res; - currency = Currency.getInstance("XXX"); - currencySymbol = "?"; - intlCurrencySymbol = "XXX"; try { res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", @@ -158,6 +170,9 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { res = null; } + currency = Currency.getInstance("XXX"); + currencySymbol = "?"; + intlCurrencySymbol = "XXX"; try { Currency localeCurrency = Currency.getInstance(loc); @@ -684,4 +699,68 @@ public class DecimalFormatSymbols implements Cloneable, Serializable serialVersionOnStream = 2; } + + /** + * Returns a {@link DecimalFormatSymbols} instance for the + * default locale obtained from either the runtime itself + * or one of the installed + * {@link java.text.spi.DecimalFormatSymbolsProvider} instances. + * This is equivalent to calling + * <code>getInstance(Locale.getDefault())</code>. + * + * @return a {@link DecimalFormatSymbols} instance for the default + * locale. + * @since 1.6 + */ + public static final DecimalFormatSymbols getInstance() + { + return getInstance(Locale.getDefault()); + } + + /** + * Returns a {@link DecimalFormatSymbols} instance for the + * specified locale obtained from either the runtime itself + * or one of the installed + * {@link java.text.spi.DecimalFormatSymbolsProvider} instances. + * + * @param locale the locale for which an instance should be + * returned. + * @return a {@link DecimalFormatSymbols} instance for the specified + * locale. + * @throws NullPointerException if <code>locale</code> is + * <code>null</code>. + * @since 1.6 + */ + public static final DecimalFormatSymbols getInstance(Locale locale) + { + try + { + if (!locale.equals(Locale.ROOT)) + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + locale, + ClassLoader.getSystemClassLoader()); + return new DecimalFormatSymbols(locale); + } + catch (MissingResourceException x) + { + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + for (DecimalFormatSymbolsProvider p : + ServiceLoader.load(DecimalFormatSymbolsProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + DecimalFormatSymbols syms = p.getInstance(locale); + if (syms != null) + return syms; + break; + } + } + } + return getInstance(LocaleHelper.getFallbackLocale(locale)); + } + } diff --git a/libjava/classpath/java/text/MessageFormat.java b/libjava/classpath/java/text/MessageFormat.java index 3d428ac7e51..ab71cecea5a 100644 --- a/libjava/classpath/java/text/MessageFormat.java +++ b/libjava/classpath/java/text/MessageFormat.java @@ -82,70 +82,72 @@ public class MessageFormat extends Format // Recompute the locale-based formatter. void setLocale (Locale loc) { - if (type == null) - ; - else if (type.equals("number")) + if (type != null) { - formatClass = java.lang.Number.class; - - if (style == null) - format = NumberFormat.getInstance(loc); - else if (style.equals("currency")) - format = NumberFormat.getCurrencyInstance(loc); - else if (style.equals("percent")) - format = NumberFormat.getPercentInstance(loc); - else if (style.equals("integer")) - { - NumberFormat nf = NumberFormat.getNumberInstance(loc); - nf.setMaximumFractionDigits(0); - nf.setGroupingUsed(false); - format = nf; - } - else - { - format = NumberFormat.getNumberInstance(loc); - DecimalFormat df = (DecimalFormat) format; - df.applyPattern(style); - } - } - else if (type.equals("time") || type.equals("date")) - { - formatClass = java.util.Date.class; - - int val = DateFormat.DEFAULT; - boolean styleIsPattern = false; - if (style == null) - ; - else if (style.equals("short")) - val = DateFormat.SHORT; - else if (style.equals("medium")) - val = DateFormat.MEDIUM; - else if (style.equals("long")) - val = DateFormat.LONG; - else if (style.equals("full")) - val = DateFormat.FULL; - else - styleIsPattern = true; - - if (type.equals("time")) - format = DateFormat.getTimeInstance(val, loc); - else - format = DateFormat.getDateInstance(val, loc); - - if (styleIsPattern) - { - SimpleDateFormat sdf = (SimpleDateFormat) format; - sdf.applyPattern(style); - } - } - else if (type.equals("choice")) - { - formatClass = java.lang.Number.class; - - if (style == null) - throw new - IllegalArgumentException ("style required for choice format"); - format = new ChoiceFormat (style); + if (type.equals("number")) + { + formatClass = java.lang.Number.class; + + if (style == null) + format = NumberFormat.getInstance(loc); + else if (style.equals("currency")) + format = NumberFormat.getCurrencyInstance(loc); + else if (style.equals("percent")) + format = NumberFormat.getPercentInstance(loc); + else if (style.equals("integer")) + { + NumberFormat nf = NumberFormat.getNumberInstance(loc); + nf.setMaximumFractionDigits(0); + nf.setGroupingUsed(false); + format = nf; + } + else + { + format = NumberFormat.getNumberInstance(loc); + DecimalFormat df = (DecimalFormat) format; + df.applyPattern(style); + } + } + else if (type.equals("time") || type.equals("date")) + { + formatClass = java.util.Date.class; + + int val = DateFormat.DEFAULT; + boolean styleIsPattern = false; + if (style != null) + { + if (style.equals("short")) + val = DateFormat.SHORT; + else if (style.equals("medium")) + val = DateFormat.MEDIUM; + else if (style.equals("long")) + val = DateFormat.LONG; + else if (style.equals("full")) + val = DateFormat.FULL; + else + styleIsPattern = true; + } + + if (type.equals("time")) + format = DateFormat.getTimeInstance(val, loc); + else + format = DateFormat.getDateInstance(val, loc); + + if (styleIsPattern) + { + SimpleDateFormat sdf = (SimpleDateFormat) format; + sdf.applyPattern(style); + } + } + else if (type.equals("choice")) + { + formatClass = java.lang.Number.class; + + if (style == null) + throw new + IllegalArgumentException ("style required for choice format"); + format = new ChoiceFormat (style); + } } } } diff --git a/libjava/classpath/java/text/NumberFormat.java b/libjava/classpath/java/text/NumberFormat.java index 1bef97ffea9..4a72f443a7b 100644 --- a/libjava/classpath/java/text/NumberFormat.java +++ b/libjava/classpath/java/text/NumberFormat.java @@ -1,5 +1,6 @@ /* NumberFormat.java -- Formats and parses numbers - Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2007 + Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,14 +39,20 @@ exception statement from your version. */ package java.text; +import gnu.java.locale.LocaleHelper; + import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; + +import java.text.spi.NumberFormatProvider; + import java.util.Currency; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; +import java.util.ServiceLoader; /** * This is the abstract superclass of all classes which format and @@ -309,17 +316,13 @@ public abstract class NumberFormat extends Format implements Cloneable private static NumberFormat computeInstance(Locale loc, String resource, String def) + throws MissingResourceException { - ResourceBundle res; - try - { - res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", - loc, ClassLoader.getSystemClassLoader()); - } - catch (MissingResourceException x) - { - res = null; - } + if (loc.equals(Locale.ROOT)) + return new DecimalFormat(def, DecimalFormatSymbols.getInstance(loc)); + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); String fmt; try { @@ -329,7 +332,7 @@ public abstract class NumberFormat extends Format implements Cloneable { fmt = def; } - DecimalFormatSymbols dfs = new DecimalFormatSymbols (loc); + DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(loc); return new DecimalFormat (fmt, dfs); } @@ -352,11 +355,33 @@ public abstract class NumberFormat extends Format implements Cloneable */ public static NumberFormat getCurrencyInstance (Locale loc) { - NumberFormat format; - - format = computeInstance (loc, "currencyFormat", "\u00A4#,##0.00;(\u00A4#,##0.00)"); - format.setMaximumFractionDigits(format.getCurrency().getDefaultFractionDigits()); - return format; + try + { + NumberFormat format; + + format = computeInstance (loc, "currencyFormat", + "\u00A4#,##0.00;(\u00A4#,##0.00)"); + format.setMaximumFractionDigits(format.getCurrency().getDefaultFractionDigits()); + return format; + } + catch (MissingResourceException e) + { + for (NumberFormatProvider p : + ServiceLoader.load(NumberFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + NumberFormat nf = p.getCurrencyInstance(loc); + if (nf != null) + return nf; + break; + } + } + } + return getCurrencyInstance(LocaleHelper.getFallbackLocale(loc)); + } } /** @@ -456,7 +481,28 @@ public abstract class NumberFormat extends Format implements Cloneable */ public static NumberFormat getNumberInstance (Locale loc) { - return computeInstance (loc, "numberFormat", "#,##0.###"); + try + { + return computeInstance (loc, "numberFormat", "#,##0.###"); + } + catch (MissingResourceException e) + { + for (NumberFormatProvider p : + ServiceLoader.load(NumberFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + NumberFormat nf = p.getNumberInstance(loc); + if (nf != null) + return nf; + break; + } + } + } + return getNumberInstance(LocaleHelper.getFallbackLocale(loc)); + } } /** @@ -484,10 +530,32 @@ public abstract class NumberFormat extends Format implements Cloneable */ public static NumberFormat getIntegerInstance(Locale locale) { - NumberFormat format = computeInstance (locale, "integerFormat", "#,##0"); - format.setMaximumFractionDigits(0); - format.setParseIntegerOnly (true); - return format; + try + { + NumberFormat format = computeInstance (locale, + "integerFormat", "#,##0"); + format.setMaximumFractionDigits(0); + format.setParseIntegerOnly (true); + return format; + } + catch (MissingResourceException e) + { + for (NumberFormatProvider p : + ServiceLoader.load(NumberFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(locale)) + { + NumberFormat nf = p.getIntegerInstance(locale); + if (nf != null) + return nf; + break; + } + } + } + return getIntegerInstance(LocaleHelper.getFallbackLocale(locale)); + } } /** @@ -511,7 +579,28 @@ public abstract class NumberFormat extends Format implements Cloneable */ public static NumberFormat getPercentInstance (Locale loc) { - return computeInstance (loc, "percentFormat", "#,##0%"); + try + { + return computeInstance (loc, "percentFormat", "#,##0%"); + } + catch (MissingResourceException e) + { + for (NumberFormatProvider p : + ServiceLoader.load(NumberFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + NumberFormat nf = p.getPercentInstance(loc); + if (nf != null) + return nf; + break; + } + } + } + return getPercentInstance(LocaleHelper.getFallbackLocale(loc)); + } } /** diff --git a/libjava/classpath/java/text/SimpleDateFormat.java b/libjava/classpath/java/text/SimpleDateFormat.java index 1e195256990..f78fdcb89c5 100644 --- a/libjava/classpath/java/text/SimpleDateFormat.java +++ b/libjava/classpath/java/text/SimpleDateFormat.java @@ -1101,11 +1101,21 @@ public class SimpleDateFormat extends DateFormat if (is_numeric) { numberFormat.setMinimumIntegerDigits(fmt_count); - if (limit_digits) - numberFormat.setMaximumIntegerDigits(fmt_count); if (maybe2DigitYear) index = pos.getIndex(); - Number n = numberFormat.parse(dateStr, pos); + Number n = null; + if (limit_digits) + { + // numberFormat.setMaximumIntegerDigits(fmt_count) may + // not work as expected. So we explicitly use substring + // of dateStr. + int origPos = pos.getIndex(); + pos.setIndex(0); + n = numberFormat.parse(dateStr.substring(origPos, origPos + fmt_count), pos); + pos.setIndex(origPos + pos.getIndex()); + } + else + n = numberFormat.parse(dateStr, pos); if (pos == null || ! (n instanceof Long)) return null; value = n.intValue() + offset; diff --git a/libjava/classpath/java/text/class-dependencies.conf b/libjava/classpath/java/text/class-dependencies.conf deleted file mode 100644 index 011b146ce10..00000000000 --- a/libjava/classpath/java/text/class-dependencies.conf +++ /dev/null @@ -1,220 +0,0 @@ -# This property file contains dependencies of classes, methods, and -# field on other methods or classes. -# -# Syntax: -# -# <used>: <needed 1> [... <needed N>] -# -# means that when <used> is included, <needed 1> (... <needed N>) must -# be included as well. -# -# <needed X> and <used> are of the form -# -# <class.methodOrField(signature)> -# -# or just -# -# <class> -# -# Within dependencies, variables can be used. A variable is defined as -# follows: -# -# {variable}: value1 value2 ... value<n> -# -# variables can be used on the right side of dependencies as follows: -# -# <used>: com.bla.blu.{variable}.Class.m()V -# -# The use of the variable will expand to <n> dependencies of the form -# -# <used>: com.bla.blu.value1.Class.m()V -# <used>: com.bla.blu.value2.Class.m()V -# ... -# <used>: com.bla.blu.value<n>.Class.m()V -# -# Variables can be redefined when building a system to select the -# required support for features like encodings, protocols, etc. -# -# Hints: -# -# - For methods and fields, the signature is mandatory. For -# specification, please see the Java Virtual Machine Specification by -# SUN. Unlike in the spec, field signatures (types) are in brackets. -# -# - Package names must be separated by '/' (and not '.'). E.g., -# java/lang/Class (this is necessary, because the '.' is used to -# separate method or field names from classes) -# -# - In case <needed> refers to a class, only the class itself will be -# included in the resulting binary, NOT necessarily all its methods -# and fields. If you want to refer to all methods and fields, you can -# write class.* as an abbreviation. -# -# - Abbreviations for packages are also possible: my/package/* means all -# methods and fields of all classes in my/package. -# -# - A line with a trailing '\' continues in the next line. - -# end of file - -# All locales supported are loaded via classes from java.text (see below) -# from class gnu/java/locale/LocaleInformation_<locale_id> -# -# This introduces a dependency for all locales. To allow an easy selection -# and addition of locales, the library variable {text_locales} can be set to -# the set of supported locales. -# - -{text_locales}: \ - af_ZA \ - ar_AE \ - ar_BH \ - ar_DZ \ - ar_EG \ - ar_IN \ - ar_IQ \ - ar_JO \ - ar_KW \ - ar_LB \ - ar_LY \ - ar_MA \ - ar_OM \ - ar_QA \ - ar_SD \ - ar_SY \ - ar_TN \ - ar_YE \ - be_BY \ - bn_IN \ - br_FR \ - bs_BA \ - ca_ES \ - cs_CZ \ - cy_GB \ - da_DK \ - de \ - de_AT \ - de_BE \ - de_CH \ - de_DE \ - de_LU \ - el_GR \ - en \ - en_AU \ - en_BW \ - en_CA \ - en_DK \ - en_GB \ - en_HK \ - en_IE \ - en_IN \ - en_NZ \ - en_PH \ - en_SG \ - en_US \ - en_ZA \ - en_ZW \ - es_AR \ - es_BO \ - es_CL \ - es_CO \ - es_CR \ - es_DO \ - es_EC \ - es_ES \ - es_GT \ - es_HN \ - es_MX \ - es_NI \ - es_PA \ - es_PE \ - es_PR \ - es_PY \ - es_SV \ - es_US \ - es_UY \ - es_VE \ - et_EE \ - eu_ES \ - fa_IR \ - fi_FI \ - fo_FO \ - fr_BE \ - fr_CA \ - fr_CH \ - fr_FR \ - fr_LU \ - ga_IE \ - gd_GB \ - gl_ES \ - gv_GB \ - he_IL \ - hi_IN \ - hr_HR \ - hu_HU \ - id_ID \ - it_CH \ - it_IT \ - iw_IL \ - ja_JP \ - ka_GE \ - kl_GL \ - ko_KR \ - kw_GB \ - lt_LT \ - lv_LV \ - mi_NZ \ - mk_MK \ - mr_IN \ - mt_MT \ - nl \ - nl_BE \ - nl_NL \ - nn_NO \ - no_NO \ - oc_FR \ - pl_PL \ - pt_BR \ - pt_PT \ - ro_RO \ - ru_RU \ - ru_UA \ - se_NO \ - sk_SK \ - sl_SI \ - sq_AL \ - sr_YU \ - sv_FI \ - sv_SE \ - ta_IN \ - te_IN \ - tg_TJ \ - tl_PH \ - tr_TR \ - uk_UA \ - ur_PK \ - uz_UZ \ - vi_VN \ - yi_US \ - zh_CN \ - zh_HK \ - zh_SG \ - zh_TW - -java/text/Collator.getInstance(Ljava/util/Locale;)Ljava/text/Collator;: \ - gnu/java/locale/LocaleInformation_{text_locales}.* - -java/text/DateFormatSymbols.<init>(Ljava/util/Locale;)V: \ - gnu/java/locale/LocaleInformation_{text_locales}.* - -java/text/DecimalFormatSymbols.<init>(Ljava/util/Locale;)V: \ - gnu/java/locale/LocaleInformation_{text_locales}.* - -java/text/BreakIterator.getInstance(Ljava/lang/String;Ljava/util/Locale;)Ljava/text/BreakIterator;: \ - gnu/java/locale/LocaleInformation_{text_locales}.* - -java/text/NumberFormat.computeInstance(Ljava/util/Locale;Ljava/lang/String;Ljava/lang/String;)Ljava/text/NumberFormat;: \ - gnu/java/locale/LocaleInformation_{text_locales}.* - -java/text/DateFormat.computeInstance(IILjava/util/Locale;ZZ)Ljava/text/DateFormat;: \ - gnu/java/locale/LocaleInformation_{text_locales}.* diff --git a/libjava/classpath/java/text/spi/BreakIteratorProvider.java b/libjava/classpath/java/text/spi/BreakIteratorProvider.java new file mode 100644 index 00000000000..7e5e056b819 --- /dev/null +++ b/libjava/classpath/java/text/spi/BreakIteratorProvider.java @@ -0,0 +1,124 @@ +/* BreakIteratorProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but 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.spi; + +import java.text.BreakIterator; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link BreakIteratorProvider} provides localized + * instances of {@link java.text.BreakIterator}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class BreakIteratorProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link BreakIteratorProvider}. + * Provided for implicit invocation by subclasses. + */ + protected BreakIteratorProvider() + { + } + + /** + * Returns a {@link java.text.BreakIterator} instance + * for character breaks in the specified + * {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for character breaks. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.BreakIterator#getCharacterInstance(java.util.Locale) + */ + public abstract BreakIterator getCharacterInstance(Locale locale); + + /** + * Returns a {@link java.text.BreakIterator} instance + * for line breaks in the specified {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for line breaks. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.BreakIterator#getLineInstance(java.util.Locale) + */ + public abstract BreakIterator getLineInstance(Locale locale); + + /** + * Returns a {@link java.text.BreakIterator} instance + * for sentence breaks in the specified + * {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for sentence breaks. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.BreakIterator#getSentenceInstance(java.util.Locale) + */ + public abstract BreakIterator getSentenceInstance(Locale locale); + + /** + * Returns a {@link java.text.BreakIterator} instance + * for word breaks in the specified + * {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for word breaks. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.BreakIterator#getWordInstance(java.util.Locale) + */ + public abstract BreakIterator getWordInstance(Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/CollatorProvider.java b/libjava/classpath/java/text/spi/CollatorProvider.java new file mode 100644 index 00000000000..6d6f409397b --- /dev/null +++ b/libjava/classpath/java/text/spi/CollatorProvider.java @@ -0,0 +1,79 @@ +/* CollatorProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but 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.spi; + +import java.text.Collator; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link CollatorProvider} provides localized + * instances of {@link java.text.Collator}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class CollatorProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link CollatorProvider}. + * Provided for implicit invocation by subclasses. + */ + protected CollatorProvider() + { + } + + /** + * Returns a {@link java.text.Collator} instance + * for the specified {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.Collator#getInstance(java.util.Locale) + */ + public abstract Collator getInstance(Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/DateFormatProvider.java b/libjava/classpath/java/text/spi/DateFormatProvider.java new file mode 100644 index 00000000000..43d54a0c60d --- /dev/null +++ b/libjava/classpath/java/text/spi/DateFormatProvider.java @@ -0,0 +1,129 @@ +/* DateFormatProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but 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.spi; + +import java.text.DateFormat; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link DateFormatProvider} provides localized + * instances of {@link java.text.DateFormat}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class DateFormatProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link DateFormatProvider}. + * Provided for implicit invocation by subclasses. + */ + protected DateFormatProvider() + { + } + + /** + * Returns a {@link java.text.DateFormat} instance + * for formatting dates with the given style in the specified + * {@link java.util.Locale}. + * + * @param style the formatting style; one of {@link DateFormat#SHORT}, + * {@link DateFormat#MEDIUM}, {@link DateFormat#LONG} + * or {@link DateFormat#FULL}. + * @param locale the desired locale. + * @return the localized instance for formatting dates. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the style is invalid or + * the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.DateFormat#getDateInstance(int,java.util.Locale) + */ + public abstract DateFormat getDateInstance(int style, + Locale locale); + + /** + * Returns a {@link java.text.DateFormat} instance + * for formatting dates and times with the given style in the + * specified {@link java.util.Locale}. + * + * @param dateStyle the date formatting style; one of + * {@link DateFormat#SHORT}, {@link DateFormat#MEDIUM}, + * {@link DateFormat#LONG} or {@link DateFormat#FULL}. + * @param timeStyle the time formatting style; one of + * {@link DateFormat#SHORT}, {@link DateFormat#MEDIUM}, + * {@link DateFormat#LONG} or {@link DateFormat#FULL}. + * @param locale the desired locale. + * @return the localized instance for formatting dates. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if either style is invalid or + * the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.DateFormat#getDateInstance(java.util.Locale) + */ + public abstract DateFormat getDateTimeInstance(int dateStyle, + int timeStyle, + Locale locale); + + /** + * Returns a {@link java.text.DateFormat} instance + * for formatting times with the given style in the specified + * {@link java.util.Locale}. + * + * @param style the formatting style; one of {@link DateFormat#SHORT}, + * {@link DateFormat#MEDIUM}, {@link DateFormat#LONG} + * or {@link DateFormat#FULL}. + * @param locale the desired locale. + * @return the localized instance for formatting times. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the style is invalid or + * the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.DateFormat#getTimeInstance(int,java.util.Locale) + */ + public abstract DateFormat getTimeInstance(int style, + Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/DateFormatSymbolsProvider.java b/libjava/classpath/java/text/spi/DateFormatSymbolsProvider.java new file mode 100644 index 00000000000..a0e97595fbe --- /dev/null +++ b/libjava/classpath/java/text/spi/DateFormatSymbolsProvider.java @@ -0,0 +1,79 @@ +/* DateFormatSymbolsProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but 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.spi; + +import java.text.DateFormatSymbols; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link DateFormatSymbolsProvider} provides localized + * instances of {@link java.text.DateFormatSymbols}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class DateFormatSymbolsProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link DateFormatSymbolsProvider}. + * Provided for implicit invocation by subclasses. + */ + protected DateFormatSymbolsProvider() + { + } + + /** + * Returns a {@link java.text.DateFormatSymbols} instance + * for the specified {@link java.util.Locale}. + * + * @param locale the locale to express the symbols in. + * @return the localized instance. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.DateFormatSymbols#getInstance(java.util.Locale) + */ + public abstract DateFormatSymbols getInstance(Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/DecimalFormatSymbolsProvider.java b/libjava/classpath/java/text/spi/DecimalFormatSymbolsProvider.java new file mode 100644 index 00000000000..d772b1eee4f --- /dev/null +++ b/libjava/classpath/java/text/spi/DecimalFormatSymbolsProvider.java @@ -0,0 +1,79 @@ +/* DecimalFormatSymbolsProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but 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.spi; + +import java.text.DecimalFormatSymbols; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link DecimalFormatSymbolsProvider} provides localized + * instances of {@link java.text.DecimalFormatSymbols}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class DecimalFormatSymbolsProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link DecimalFormatSymbolsProvider}. + * Provided for implicit invocation by subclasses. + */ + protected DecimalFormatSymbolsProvider() + { + } + + /** + * Returns a {@link java.text.DecimalFormatSymbols} instance + * for the specified {@link java.util.Locale}. + * + * @param locale the locale to express the symbols in. + * @return the localized instance. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.DecimalFormatSymbols#getInstance(java.util.Locale) + */ + public abstract DecimalFormatSymbols getInstance(Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/NumberFormatProvider.java b/libjava/classpath/java/text/spi/NumberFormatProvider.java new file mode 100644 index 00000000000..2a252701f4a --- /dev/null +++ b/libjava/classpath/java/text/spi/NumberFormatProvider.java @@ -0,0 +1,129 @@ +/* NumberFormatProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but 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.spi; + +import java.text.NumberFormat; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link NumberFormatProvider} provides localized + * instances of {@link java.text.NumberFormat}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class NumberFormatProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link NumberFormatProvider}. + * Provided for implicit invocation by subclasses. + */ + protected NumberFormatProvider() + { + } + + /** + * Returns a {@link java.text.NumberFormat} instance + * for monetary values in the specified + * {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for monetary values. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.NumberFormat#getCurrencyInstance(java.util.Locale) + */ + public abstract NumberFormat getCurrencyInstance(Locale locale); + + /** + * Returns a {@link java.text.NumberFormat} instance + * for integers in the specified {@link java.util.Locale}. + * The returned instance should be configured to round + * floating point numbers to the nearest integer using + * {@link java.math.RoundingMode#HALF_EVEN} rounding, + * and to parse only the integer part of a number. + * + * @param locale the desired locale. + * @return the localized instance for integers. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.NumberFormat#getIntegerInstance(java.util.Locale) + * @see java.math.RoundingMode#HALF_EVEN + * @see java.text.NumberFormat#isParseIntegerOnly() + */ + public abstract NumberFormat getIntegerInstance(Locale locale); + + /** + * Returns a general-purpose {@link java.text.NumberFormat} + * instance in the specified {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return a general-purpose localized instance. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.NumberFormat#getNumberInstance(java.util.Locale) + */ + public abstract NumberFormat getNumberInstance(Locale locale); + + /** + * Returns a {@link java.text.NumberFormat} instance + * for percentage values in the specified + * {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for percentage values. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.NumberFormat#getPercentInstance(java.util.Locale) + */ + public abstract NumberFormat getPercentInstance(Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/package.html b/libjava/classpath/java/text/spi/package.html new file mode 100644 index 00000000000..7f5232ce09c --- /dev/null +++ b/libjava/classpath/java/text/spi/package.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.text.spi package. + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.text.spi</title></head> + +<body> + +<p> +A series of service provider interfaces for use by the +classes in <code>java.text</code>. +</p> +<p><span style="font-weight: bold;">Since</span>: 1.6</p> +</body> +</html> diff --git a/libjava/classpath/java/util/AbstractMap.java b/libjava/classpath/java/util/AbstractMap.java index 29249e1dc22..2f58121cede 100644 --- a/libjava/classpath/java/util/AbstractMap.java +++ b/libjava/classpath/java/util/AbstractMap.java @@ -69,10 +69,23 @@ import java.io.Serializable; */ public abstract class AbstractMap<K, V> implements Map<K, V> { - /** @since 1.6 */ + /** + * A class containing an immutable key and value. The + * implementation of {@link Entry#setValue(V)} for this class + * simply throws an {@link UnsupportedOperationException}, + * thus preventing changes being made. This is useful when + * a static thread-safe view of a map is required. + * + * @since 1.6 + */ public static class SimpleImmutableEntry<K, V> implements Entry<K, V>, Serializable { + /** + * Compatible with JDK 1.6 + */ + private static final long serialVersionUID = 7138329143949025153L; + K key; V value; @@ -670,6 +683,12 @@ public abstract class AbstractMap<K, V> implements Map<K, V> */ public static class SimpleEntry<K, V> implements Entry<K, V>, Serializable { + + /** + * Compatible with JDK 1.6 + */ + private static final long serialVersionUID = -8499721149061103585L; + /** * The key. Package visible for direct manipulation. */ @@ -730,7 +749,7 @@ public abstract class AbstractMap<K, V> implements Map<K, V> * * @return the key */ - public final K getKey() + public K getKey() { return key; } @@ -741,7 +760,7 @@ public abstract class AbstractMap<K, V> implements Map<K, V> * * @return the value */ - public final V getValue() + public V getValue() { return value; } @@ -755,7 +774,7 @@ public abstract class AbstractMap<K, V> implements Map<K, V> * * @return the hash code */ - public final int hashCode() + public int hashCode() { return (AbstractMap.hashCode(key) ^ AbstractMap.hashCode(value)); } @@ -788,7 +807,7 @@ public abstract class AbstractMap<K, V> implements Map<K, V> * * @return the string representation */ - public final String toString() + public String toString() { return key + "=" + value; } diff --git a/libjava/classpath/java/util/Arrays.java b/libjava/classpath/java/util/Arrays.java index 41e80455104..9443ced5bdd 100644 --- a/libjava/classpath/java/util/Arrays.java +++ b/libjava/classpath/java/util/Arrays.java @@ -92,8 +92,38 @@ public class Arrays */ public static int binarySearch(byte[] a, byte key) { - int low = 0; - int hi = a.length - 1; + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a byte array for a key. The range + * must be sorted (as by the <code>sort(byte[], int, int)</code> method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if <code>low > hi</code> + * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or + * <code>hi > a.length</code>. + */ + public static int binarySearch(byte[] a, int low, int hi, byte key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); int mid = 0; while (low <= hi) { @@ -126,8 +156,38 @@ public class Arrays */ public static int binarySearch(char[] a, char key) { - int low = 0; - int hi = a.length - 1; + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a char array for a key. The range + * must be sorted (as by the <code>sort(char[], int, int)</code> method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if <code>low > hi</code> + * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or + * <code>hi > a.length</code>. + */ + public static int binarySearch(char[] a, int low, int hi, char key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); int mid = 0; while (low <= hi) { @@ -160,8 +220,38 @@ public class Arrays */ public static int binarySearch(short[] a, short key) { - int low = 0; - int hi = a.length - 1; + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a short array for a key. The range + * must be sorted (as by the <code>sort(short[], int, int)</code> method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if <code>low > hi</code> + * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or + * <code>hi > a.length</code>. + */ + public static int binarySearch(short[] a, int low, int hi, short key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); int mid = 0; while (low <= hi) { @@ -194,8 +284,38 @@ public class Arrays */ public static int binarySearch(int[] a, int key) { - int low = 0; - int hi = a.length - 1; + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of an integer array for a key. The range + * must be sorted (as by the <code>sort(int[], int, int)</code> method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if <code>low > hi</code> + * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or + * <code>hi > a.length</code>. + */ + public static int binarySearch(int[] a, int low, int hi, int key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); int mid = 0; while (low <= hi) { @@ -228,8 +348,38 @@ public class Arrays */ public static int binarySearch(long[] a, long key) { - int low = 0; - int hi = a.length - 1; + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a long array for a key. The range + * must be sorted (as by the <code>sort(long[], int, int)</code> method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if <code>low > hi</code> + * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or + * <code>hi > a.length</code>. + */ + public static int binarySearch(long[] a, int low, int hi, long key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); int mid = 0; while (low <= hi) { @@ -262,9 +412,39 @@ public class Arrays */ public static int binarySearch(float[] a, float key) { + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a float array for a key. The range + * must be sorted (as by the <code>sort(float[], int, int)</code> method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if <code>low > hi</code> + * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or + * <code>hi > a.length</code>. + */ + public static int binarySearch(float[] a, int low, int hi, float key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); // Must use Float.compare to take into account NaN, +-0. - int low = 0; - int hi = a.length - 1; int mid = 0; while (low <= hi) { @@ -297,9 +477,39 @@ public class Arrays */ public static int binarySearch(double[] a, double key) { + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a double array for a key. The range + * must be sorted (as by the <code>sort(double[], int, int)</code> method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if <code>low > hi</code> + * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or + * <code>hi > a.length</code>. + */ + public static int binarySearch(double[] a, int low, int hi, double key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); // Must use Double.compare to take into account NaN, +-0. - int low = 0; - int hi = a.length - 1; int mid = 0; while (low <= hi) { @@ -337,10 +547,33 @@ public class Arrays */ public static int binarySearch(Object[] a, Object key) { + if (a.length == 0) + return -1; return binarySearch(a, key, null); } /** + * Perform a binary search of a range of an Object array for a key. The range + * must be sorted (as by the <code>sort(Object[], int, int)</code> method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(Object[] a, int low, int hi, Object key) + { + return binarySearch(a, low, hi, key, null); + } + + /** * Perform a binary search of an Object array for a key, using a supplied * Comparator. The array must be sorted (as by the sort() method with the * same Comparator) - if it is not, the behaviour of this method is @@ -364,8 +597,44 @@ public class Arrays */ public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c) { - int low = 0; - int hi = a.length - 1; + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key, c); + } + + /** + * Perform a binary search of a range of an Object array for a key using + * a {@link Comparator}. The range must be sorted (as by the + * <code>sort(Object[], int, int)</code> method) - if it is not, the + * behaviour of this method is undefined, and may be an infinite loop. If + * the array contains the key more than once, any one of them may be found. + * Note: although the specification allows for an infinite loop if the array + * is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @param c the comparator by which the array is sorted; or null to + * use the elements' natural order + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws ClassCastException if key could not be compared with one of the + * elements of a + * @throws IllegalArgumentException if <code>low > hi</code> + * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or + * <code>hi > a.length</code>. + */ + public static <T> int binarySearch(T[] a, int low, int hi, T key, + Comparator<? super T> c) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); int mid = 0; while (low <= hi) { @@ -3062,4 +3331,701 @@ public class Arrays return array; } } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with <code>false</code> to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return <code>false</code>. + * This is equivalent to calling + * <code>copyOfRange(original, 0, newLength)</code>. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * <code>false</code> to obtain the required length. + * @throws NegativeArraySizeException if <code>newLength</code> is negative. + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOfRange(boolean[],int,int) + */ + public static boolean[] copyOf(boolean[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with <code>false</code> + * if <code>to</code> is greater than the length of the original + * array. <code>from</code> must be in the range zero to + * <code>original.length</code> and can not be greater than + * <code>to</code>. The initial element of the + * returned array will be equal to <code>original[from]</code>, + * except where <code>from</code> is equal to <code>to</code> + * (where a zero-length array will be returned) or <code> + * <code>from</code> is equal to <code>original.length</code> + * (where an array padded with <code>false</code> will be + * returned). The returned array is always of length + * <code>to - from</code>. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code> + * or <code>from > original.length</code> + * @throws IllegalArgumentException if <code>from > to</code> + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOf(boolean[],int) + */ + public static boolean[] copyOfRange(boolean[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + boolean[] newArray = new boolean[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, false); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with <code>(byte)0</code> to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return <code>(byte)0</code>. + * This is equivalent to calling + * <code>copyOfRange(original, 0, newLength)</code>. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * <code>(byte)0</code> to obtain the required length. + * @throws NegativeArraySizeException if <code>newLength</code> is negative. + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOfRange(byte[],int,int) + */ + public static byte[] copyOf(byte[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with <code>(byte)0</code> + * if <code>to</code> is greater than the length of the original + * array. <code>from</code> must be in the range zero to + * <code>original.length</code> and can not be greater than + * <code>to</code>. The initial element of the + * returned array will be equal to <code>original[from]</code>, + * except where <code>from</code> is equal to <code>to</code> + * (where a zero-length array will be returned) or <code> + * <code>from</code> is equal to <code>original.length</code> + * (where an array padded with <code>(byte)0</code> will be + * returned). The returned array is always of length + * <code>to - from</code>. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code> + * or <code>from > original.length</code> + * @throws IllegalArgumentException if <code>from > to</code> + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOf(byte[],int) + */ + public static byte[] copyOfRange(byte[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + byte[] newArray = new byte[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, (byte)0); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with <code>'\0'</code> to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return <code>'\0'</code>. + * This is equivalent to calling + * <code>copyOfRange(original, 0, newLength)</code>. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * <code>'\0'</code> to obtain the required length. + * @throws NegativeArraySizeException if <code>newLength</code> is negative. + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOfRange(char[],int,int) + */ + public static char[] copyOf(char[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with <code>'\0'</code> + * if <code>to</code> is greater than the length of the original + * array. <code>from</code> must be in the range zero to + * <code>original.length</code> and can not be greater than + * <code>to</code>. The initial element of the + * returned array will be equal to <code>original[from]</code>, + * except where <code>from</code> is equal to <code>to</code> + * (where a zero-length array will be returned) or <code> + * <code>from</code> is equal to <code>original.length</code> + * (where an array padded with <code>'\0'</code> will be + * returned). The returned array is always of length + * <code>to - from</code>. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code> + * or <code>from > original.length</code> + * @throws IllegalArgumentException if <code>from > to</code> + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOf(char[],int) + */ + public static char[] copyOfRange(char[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + char[] newArray = new char[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, '\0'); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with <code>0d</code> to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return <code>0d</code>. + * This is equivalent to calling + * <code>copyOfRange(original, 0, newLength)</code>. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * <code>0d</code> to obtain the required length. + * @throws NegativeArraySizeException if <code>newLength</code> is negative. + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOfRange(double[],int,int) + */ + public static double[] copyOf(double[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with <code>0d</code> + * if <code>to</code> is greater than the length of the original + * array. <code>from</code> must be in the range zero to + * <code>original.length</code> and can not be greater than + * <code>to</code>. The initial element of the + * returned array will be equal to <code>original[from]</code>, + * except where <code>from</code> is equal to <code>to</code> + * (where a zero-length array will be returned) or <code> + * <code>from</code> is equal to <code>original.length</code> + * (where an array padded with <code>0d</code> will be + * returned). The returned array is always of length + * <code>to - from</code>. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code> + * or <code>from > original.length</code> + * @throws IllegalArgumentException if <code>from > to</code> + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOf(double[],int) + */ + public static double[] copyOfRange(double[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + double[] newArray = new double[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, 0d); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with <code>0f</code> to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return <code>0f</code>. + * This is equivalent to calling + * <code>copyOfRange(original, 0, newLength)</code>. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * <code>0f</code> to obtain the required length. + * @throws NegativeArraySizeException if <code>newLength</code> is negative. + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOfRange(float[],int,int) + */ + public static float[] copyOf(float[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with <code>0f</code> + * if <code>to</code> is greater than the length of the original + * array. <code>from</code> must be in the range zero to + * <code>original.length</code> and can not be greater than + * <code>to</code>. The initial element of the + * returned array will be equal to <code>original[from]</code>, + * except where <code>from</code> is equal to <code>to</code> + * (where a zero-length array will be returned) or <code> + * <code>from</code> is equal to <code>original.length</code> + * (where an array padded with <code>0f</code> will be + * returned). The returned array is always of length + * <code>to - from</code>. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code> + * or <code>from > original.length</code> + * @throws IllegalArgumentException if <code>from > to</code> + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOf(float[],int) + */ + public static float[] copyOfRange(float[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + float[] newArray = new float[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, 0f); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with <code>0</code> to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return <code>0</code>. + * This is equivalent to calling + * <code>copyOfRange(original, 0, newLength)</code>. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * <code>0</code> to obtain the required length. + * @throws NegativeArraySizeException if <code>newLength</code> is negative. + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOfRange(int[],int,int) + */ + public static int[] copyOf(int[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with <code>0</code> + * if <code>to</code> is greater than the length of the original + * array. <code>from</code> must be in the range zero to + * <code>original.length</code> and can not be greater than + * <code>to</code>. The initial element of the + * returned array will be equal to <code>original[from]</code>, + * except where <code>from</code> is equal to <code>to</code> + * (where a zero-length array will be returned) or <code> + * <code>from</code> is equal to <code>original.length</code> + * (where an array padded with <code>0</code> will be + * returned). The returned array is always of length + * <code>to - from</code>. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code> + * or <code>from > original.length</code> + * @throws IllegalArgumentException if <code>from > to</code> + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOf(int[],int) + */ + public static int[] copyOfRange(int[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + int[] newArray = new int[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, 0); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with <code>0L</code> to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return <code>0L</code>. + * This is equivalent to calling + * <code>copyOfRange(original, 0, newLength)</code>. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * <code>0L</code> to obtain the required length. + * @throws NegativeArraySizeException if <code>newLength</code> is negative. + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOfRange(long[],int,int) + */ + public static long[] copyOf(long[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with <code>0L</code> + * if <code>to</code> is greater than the length of the original + * array. <code>from</code> must be in the range zero to + * <code>original.length</code> and can not be greater than + * <code>to</code>. The initial element of the + * returned array will be equal to <code>original[from]</code>, + * except where <code>from</code> is equal to <code>to</code> + * (where a zero-length array will be returned) or <code> + * <code>from</code> is equal to <code>original.length</code> + * (where an array padded with <code>0L</code> will be + * returned). The returned array is always of length + * <code>to - from</code>. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code> + * or <code>from > original.length</code> + * @throws IllegalArgumentException if <code>from > to</code> + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOf(long[],int) + */ + public static long[] copyOfRange(long[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + long[] newArray = new long[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, 0L); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with <code>(short)0</code> to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return <code>(short)0</code>. + * This is equivalent to calling + * <code>copyOfRange(original, 0, newLength)</code>. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * <code>(short)0</code> to obtain the required length. + * @throws NegativeArraySizeException if <code>newLength</code> is negative. + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOfRange(short[],int,int) + */ + public static short[] copyOf(short[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with <code>(short)0</code> + * if <code>to</code> is greater than the length of the original + * array. <code>from</code> must be in the range zero to + * <code>original.length</code> and can not be greater than + * <code>to</code>. The initial element of the + * returned array will be equal to <code>original[from]</code>, + * except where <code>from</code> is equal to <code>to</code> + * (where a zero-length array will be returned) or <code> + * <code>from</code> is equal to <code>original.length</code> + * (where an array padded with <code>(short)0</code> will be + * returned). The returned array is always of length + * <code>to - from</code>. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code> + * or <code>from > original.length</code> + * @throws IllegalArgumentException if <code>from > to</code> + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOf(short[],int) + */ + public static short[] copyOfRange(short[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + short[] newArray = new short[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, (short)0); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with <code>null</code> to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return <code>null</code>. + * This is equivalent to calling + * <code>copyOfRange(original, 0, newLength)</code>. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * <code>null</code> to obtain the required length. + * @throws NegativeArraySizeException if <code>newLength</code> is negative. + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOfRange(T[],int,int) + */ + public static <T> T[] copyOf(T[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with <code>null</code> + * if <code>to</code> is greater than the length of the original + * array. <code>from</code> must be in the range zero to + * <code>original.length</code> and can not be greater than + * <code>to</code>. The initial element of the + * returned array will be equal to <code>original[from]</code>, + * except where <code>from</code> is equal to <code>to</code> + * (where a zero-length array will be returned) or <code> + * <code>from</code> is equal to <code>original.length</code> + * (where an array padded with <code>null</code> will be + * returned). The returned array is always of length + * <code>to - from</code>. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code> + * or <code>from > original.length</code> + * @throws IllegalArgumentException if <code>from > to</code> + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOf(T[],int) + */ + public static <T> T[] copyOfRange(T[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + T[] newArray = (T[]) new Object[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, null); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with <code>null</code> to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return <code>null</code>. + * This is equivalent to calling + * <code>copyOfRange(original, 0, newLength, newType)</code>. The returned + * array will be of the specified type, <code>newType</code>. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @param newType the type of the returned array. + * @return a copy of the original array, truncated or padded with + * <code>null</code> to obtain the required length. + * @throws NegativeArraySizeException if <code>newLength</code> is negative. + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOfRange(U[],int,int,Class) + */ + public static <T,U> T[] copyOf(U[] original, int newLength, + Class<? extends T[]> newType) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength, newType); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with <code>null</code> + * if <code>to</code> is greater than the length of the original + * array. <code>from</code> must be in the range zero to + * <code>original.length</code> and can not be greater than + * <code>to</code>. The initial element of the + * returned array will be equal to <code>original[from]</code>, + * except where <code>from</code> is equal to <code>to</code> + * (where a zero-length array will be returned) or <code> + * <code>from</code> is equal to <code>original.length</code> + * (where an array padded with <code>null</code> will be + * returned). The returned array is always of length + * <code>to - from</code> and will be of the specified type, + * <code>newType</code>. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @param newType the type of the returned array. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code> + * or <code>from > original.length</code> + * @throws IllegalArgumentException if <code>from > to</code> + * @throws NullPointerException if <code>original</code> is <code>null</code>. + * @since 1.6 + * @see #copyOf(T[],int) + */ + public static <T,U> T[] copyOfRange(U[] original, int from, int to, + Class<? extends T[]> newType) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + T[] newArray = (T[]) Array.newInstance(newType.getComponentType(), + to - from); + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, null); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } } diff --git a/libjava/classpath/java/util/Calendar.java b/libjava/classpath/java/util/Calendar.java index 8c46c01936c..2b385b1a0b2 100644 --- a/libjava/classpath/java/util/Calendar.java +++ b/libjava/classpath/java/util/Calendar.java @@ -43,9 +43,12 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.text.DateFormatSymbols; + /** * This class is an abstract base class for Calendars, which can be * used to convert between <code>Date</code> objects and a set of @@ -99,6 +102,20 @@ day_of_week + week_of_year</pre> * specific field by one, propagating overflows), or * <code>add</code>ing/substracting a fixed amount to a field. * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Jochen Hoenicke (Jochen.Hoenicke@Informatik.Uni-Oldenburg.de) + * @author Warren Levy (warrenl@cygnus.com) + * @author Jeff Sturm (jsturm@one-point.com) + * @author Tom Tromey (tromey@redhat.com) + * @author Bryce McKinlay (mckinlay@redhat.com) + * @author Ingo Proetel (proetel@aicas.com) + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Noa Resare (noa@resare.com) + * @author Sven de Marothy (sven@physto.se) + * @author David Gilbert (david.gilbert@object-refinery.com) + * @author Olivier Jolly (olivier.jolly@pcedev.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) * @see Date * @see GregorianCalendar * @see TimeZone @@ -326,6 +343,34 @@ public abstract class Calendar public static final int PM = 1; /** + * A style specifier for {@link #getDisplayNames(int,int,Locale)} + * stating that names should be returned in both long and short variants. + * + * @since 1.6 + * @see #SHORT + * @see #LONG + */ + public static final int ALL_STYLES = 0; + + /** + * A style specifier for {@link #getDisplayName(int,int,Locale)} + * and {@link #getDisplayNames(int,int,Locale)} stating that names + * should be returned in their short variant if applicable. + * + * @since 1.6 + */ + public static final int SHORT = 1; + + /** + * A style specifier for {@link #getDisplayName(int,int,Locale)} + * and {@link #getDisplayNames(int,int,Locale)} stating that names + * should be returned in their long variant if applicable. + * + * @since 1.6 + */ + public static final int LONG = 2; + + /** * The time fields. The array is indexed by the constants YEAR to * DST_OFFSET. * @serial @@ -527,7 +572,7 @@ public abstract class Calendar * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle * lookup for every getInstance call. */ - private static HashMap cache = new HashMap(); + private static HashMap<Locale,Class> cache = new HashMap<Locale,Class>(); /** Preset argument types for calendar-class constructor lookup. */ private static Class[] ctorArgTypes = new Class[] @@ -549,7 +594,7 @@ public abstract class Calendar */ public static synchronized Calendar getInstance(TimeZone zone, Locale locale) { - Class calendarClass = (Class) cache.get(locale); + Class calendarClass = cache.get(locale); Throwable exception = null; try @@ -1343,4 +1388,205 @@ public abstract class Calendar areFieldsSet = false; } } + + /** + * Returns a localised textual representation of the current value + * of the given field using the specified style. If there is no + * applicable textual representation (e.g. the field has a numeric + * value), then <code>null</code> is returned. If one does exist, + * then the value is obtained from {@link #get(int)} and converted + * appropriately. For example, if the <code>MONTH</code> field is + * requested, then <code>get(MONTH)</code> is called. This is then + * converted to a textual representation based on its value and + * the style requested; if the <code>LONG</code> style is requested + * and the returned value is <code>11</code> from a + * {@link GregorianCalendar} implementation, then <code>"December"</code> + * is returned. By default, a textual representation is available + * for all fields which have an applicable value obtainable from + * {@link java.text.DateFormatSymbols}. + * + * @param field the calendar field whose textual representation should + * be obtained. + * @param style the style to use; either {@link #LONG} or {@link #SHORT}. + * @param locale the locale to use for translation. + * @return the textual representation of the given field in the specified + * style, or <code>null</code> if none is applicable. + * @throws IllegalArgumentException if <code>field</code> or <code>style</code> + * or invalid, or the calendar is non-lenient + * and has invalid values. + * @throws NullPointerException if <code>locale</code> is <code>null</code>. + * @since 1.6 + */ + public String getDisplayName(int field, int style, Locale locale) + { + if (field < 0 || field >= FIELD_COUNT) + throw new IllegalArgumentException("The field value, " + field + + ", is invalid."); + if (style != SHORT && style != LONG) + throw new IllegalArgumentException("The style must be either " + + "short or long."); + if (field == YEAR || field == WEEK_OF_YEAR || + field == WEEK_OF_MONTH || field == DAY_OF_MONTH || + field == DAY_OF_YEAR || field == DAY_OF_WEEK_IN_MONTH || + field == HOUR || field == HOUR_OF_DAY || field == MINUTE || + field == SECOND || field == MILLISECOND) + return null; + + int value = get(field); + DateFormatSymbols syms = DateFormatSymbols.getInstance(locale); + if (field == ERA) + return syms.getEras()[value]; + if (field == MONTH) + if (style == LONG) + return syms.getMonths()[value]; + else + return syms.getShortMonths()[value]; + if (field == DAY_OF_WEEK) + if (style == LONG) + return syms.getWeekdays()[value]; + else + return syms.getShortWeekdays()[value]; + if (field == AM_PM) + return syms.getAmPmStrings()[value]; + if (field == ZONE_OFFSET) + if (style == LONG) + return syms.getZoneStrings()[value][1]; + else + return syms.getZoneStrings()[value][2]; + if (field == DST_OFFSET) + if (style == LONG) + return syms.getZoneStrings()[value][3]; + else + return syms.getZoneStrings()[value][4]; + + throw new InternalError("Failed to resolve field " + field + + " with style " + style + " for locale " + + locale); + } + + /** + * Returns a map linking all specified textual representations + * of the given field to their numerical values. The textual + * representations included are determined by the specified + * style and locale. For example, if the style <code>LONG</code> + * is specified and the German locale, then the map will + * contain "Montag" to {@link #MONDAY}, "Dienstag" to + * {@link #TUESDAY}, "Mittwoch" to {@link #WEDNESDAY} and + * so on. The default implementation uses the values returned + * by {@link DateFormatSymbols} so, for example, the style + * {@link #ALL_STYLES} and the field {@link #MONTH} will return + * a map filled with the values returned from + * {@link DateFormatSymbols#getMonths()} and + * {@link DateFormatSymbols#getShortMonths()}. If there are + * no textual representations for a given field (usually because + * it is purely numeric, such as the year in the + * {@link GregorianCalendar}), <code>null</code> is returned. + * + * @param field the calendar field whose textual representation should + * be obtained. + * @param style the style to use; either {@link #LONG}, {@link #SHORT} + * or {@link ALL_STYLES}. + * @param locale the locale to use for translation. + * @return a map of the textual representations of the given field in the + * specified style to their numeric values, or <code>null</code> + * if none is applicable. + * @throws IllegalArgumentException if <code>field</code> or <code>style</code> + * or invalid, or the calendar is non-lenient + * and has invalid values. + * @throws NullPointerException if <code>locale</code> is <code>null</code>. + * @since 1.6 + */ + public Map<String,Integer> getDisplayNames(int field, int style, Locale locale) + { + if (field < 0 || field >= FIELD_COUNT) + throw new IllegalArgumentException("The field value, " + field + + ", is invalid."); + if (style != SHORT && style != LONG && style != ALL_STYLES) + throw new IllegalArgumentException("The style must be either " + + "short, long or all styles."); + if (field == YEAR || field == WEEK_OF_YEAR || + field == WEEK_OF_MONTH || field == DAY_OF_MONTH || + field == DAY_OF_YEAR || field == DAY_OF_WEEK_IN_MONTH || + field == HOUR || field == HOUR_OF_DAY || field == MINUTE || + field == SECOND || field == MILLISECOND) + return null; + + DateFormatSymbols syms = DateFormatSymbols.getInstance(locale); + Map<String,Integer> map = new HashMap<String,Integer>(); + if (field == ERA) + { + String[] eras = syms.getEras(); + for (int a = 0; a < eras.length; ++a) + map.put(eras[a], a); + return map; + } + if (field == MONTH) + { + if (style == LONG || style == ALL_STYLES) + { + String[] months = syms.getMonths(); + for (int a = 0; a < months.length; ++a) + map.put(months[a], a); + } + if (style == SHORT || style == ALL_STYLES) + { + String[] months = syms.getShortMonths(); + for (int a = 0; a < months.length; ++a) + map.put(months[a], a); + } + return map; + } + if (field == DAY_OF_WEEK) + { + if (style == LONG || style == ALL_STYLES) + { + String[] weekdays = syms.getWeekdays(); + for (int a = SUNDAY; a < weekdays.length; ++a) + map.put(weekdays[a], a); + } + if (style == SHORT || style == ALL_STYLES) + { + String[] weekdays = syms.getShortWeekdays(); + for (int a = SUNDAY; a < weekdays.length; ++a) + map.put(weekdays[a], a); + } + return map; + } + if (field == AM_PM) + { + String[] ampms = syms.getAmPmStrings(); + for (int a = 0; a < ampms.length; ++a) + map.put(ampms[a], a); + return map; + } + if (field == ZONE_OFFSET) + { + String[][] zones = syms.getZoneStrings(); + for (int a = 0; a < zones.length; ++a) + { + if (style == LONG || style == ALL_STYLES) + map.put(zones[a][1], a); + if (style == SHORT || style == ALL_STYLES) + map.put(zones[a][2], a); + } + return map; + } + if (field == DST_OFFSET) + { + String[][] zones = syms.getZoneStrings(); + for (int a = 0; a < zones.length; ++a) + { + if (style == LONG || style == ALL_STYLES) + map.put(zones[a][3], a); + if (style == SHORT || style == ALL_STYLES) + map.put(zones[a][4], a); + } + return map; + } + + throw new InternalError("Failed to resolve field " + field + + " with style " + style + " for locale " + + locale); + } + } diff --git a/libjava/classpath/java/util/Collections.java b/libjava/classpath/java/util/Collections.java index 77ff6ed8fa0..fd802fe9db4 100644 --- a/libjava/classpath/java/util/Collections.java +++ b/libjava/classpath/java/util/Collections.java @@ -706,14 +706,16 @@ public class Collections { if (!forward) itr.next(); // Changing direction first. - for ( ; i != pos; i++, o = itr.next()); + for ( ; i != pos; i++, o = itr.next()) + ; forward = true; } else { if (forward) itr.previous(); // Changing direction first. - for ( ; i != pos; i--, o = itr.previous()); + for ( ; i != pos; i--, o = itr.previous()) + ; forward = false; } final int d = compare(o, key, c); @@ -1460,8 +1462,10 @@ public class Collections public static int frequency (Collection<?> c, Object o) { int result = 0; - for (Object v : c) + final Iterator<?> it = c.iterator(); + while (it.hasNext()) { + Object v = it.next(); if (AbstractCollection.equals(o, v)) ++result; } @@ -1522,8 +1526,9 @@ public class Collections public static boolean disjoint(Collection<?> c1, Collection<?> c2) { Collection<Object> oc1 = (Collection<Object>) c1; - for (Object o : oc1) - if (c2.contains(o)) + final Iterator<Object> it = oc1.iterator(); + while (it.hasNext()) + if (c2.contains(it.next())) return false; return true; } @@ -5113,7 +5118,7 @@ public class Collections // The array returned is an array of UnmodifiableMapEntry instead of // Map.Entry - public Map.Entry<K,V>[] toArray() + public Object[] toArray() { Object[] mapEntryResult = super.toArray(); UnmodifiableMapEntry<K,V> result[] = null; @@ -5127,7 +5132,7 @@ public class Collections } return result; } - + // The array returned is an array of UnmodifiableMapEntry instead of // Map.Entry public <S> S[] toArray(S[] array) @@ -5825,8 +5830,10 @@ public class Collections public boolean addAll(Collection<? extends E> coll) { Collection<E> typedColl = (Collection<E>) c; - for (E element : typedColl) + final Iterator<E> it = typedColl.iterator(); + while (it.hasNext()) { + final E element = it.next(); if (!type.isInstance(element)) throw new ClassCastException("A member of the collection is not of the correct type."); } @@ -6167,9 +6174,10 @@ public class Collections public boolean addAll(int index, Collection<? extends E> coll) { Collection<E> typedColl = (Collection<E>) coll; - for (E element : typedColl) + final Iterator<E> it = typedColl.iterator(); + while (it.hasNext()) { - if (!type.isInstance(element)) + if (!type.isInstance(it.next())) throw new ClassCastException("A member of the collection is not of the correct type."); } return list.addAll(index, coll); @@ -6870,8 +6878,10 @@ public class Collections public void putAll(Map<? extends K, ? extends V> map) { Map<K,V> typedMap = (Map<K,V>) map; - for (Map.Entry<K,V> entry : typedMap.entrySet()) + final Iterator<Map.Entry<K,V>> it = typedMap.entrySet().iterator(); + while (it.hasNext()) { + final Map.Entry<K,V> entry = it.next(); if (!keyType.isInstance(entry.getKey())) throw new ClassCastException("A key is of the wrong type."); if (!valueType.isInstance(entry.getValue())) @@ -7423,4 +7433,189 @@ public class Collections } } // class CheckedSortedSet + /** + * Returns a view of a {@link Deque} as a stack or LIFO (Last-In-First-Out) + * {@link Queue}. Each call to the LIFO queue corresponds to one + * equivalent method call to the underlying deque, with the exception + * of {@link Collection#addAll(Collection)}, which is emulated by a series + * of {@link Deque#push(E)} calls. + * + * @param deque the deque to convert to a LIFO queue. + * @return a LIFO queue. + * @since 1.6 + */ + public static <T> Queue<T> asLifoQueue(Deque<T> deque) + { + return new LIFOQueue<T>(deque); + } + + /** + * Returns a set backed by the supplied map. The resulting set + * has the same performance, concurrency and ordering characteristics + * as the original map. The supplied map must be empty and should not + * be used after the set is created. Each call to the set corresponds + * to one equivalent method call to the underlying map, with the exception + * of {@link Set#addAll(Collection)} which is emulated by a series of + * calls to <code>put</code>. + * + * @param map the map to convert to a set. + * @return a set backed by the supplied map. + * @throws IllegalArgumentException if the map is not empty. + * @since 1.6 + */ + public static <E> Set<E> newSetFromMap(Map<E,Boolean> map) + { + return new MapSet<E>(map); + } + + /** + * The implementation of {@link #asLIFOQueue(Deque)}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ + private static class LIFOQueue<T> + extends AbstractQueue<T> + { + + /** + * The backing deque. + */ + private Deque<T> deque; + + /** + * Constructs a new {@link LIFOQueue} with the specified + * backing {@link Deque}. + * + * @param deque the backing deque. + */ + public LIFOQueue(Deque<T> deque) + { + this.deque = deque; + } + + public boolean add(T e) + { + return deque.offerFirst(e); + } + + public boolean addAll(Collection<? extends T> c) + { + boolean result = false; + final Iterator<? extends T> it = c.iterator(); + while (it.hasNext()) + result |= deque.offerFirst(it.next()); + return result; + } + + public void clear() + { + deque.clear(); + } + + public boolean isEmpty() + { + return deque.isEmpty(); + } + + public Iterator<T> iterator() + { + return deque.iterator(); + } + + public boolean offer(T e) + { + return deque.offerFirst(e); + } + + public T peek() + { + return deque.peek(); + } + + public T poll() + { + return deque.poll(); + } + + public int size() + { + return deque.size(); + } + } // class LIFOQueue + + /** + * The implementation of {@link #newSetFromMap(Map)}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ + private static class MapSet<E> + extends AbstractSet<E> + { + + /** + * The backing map. + */ + private Map<E,Boolean> map; + + /** + * Constructs a new {@link MapSet} using the specified + * backing {@link Map}. + * + * @param map the backing map. + * @throws IllegalArgumentException if the map is not empty. + */ + public MapSet(Map<E,Boolean> map) + { + if (!map.isEmpty()) + throw new IllegalArgumentException("The map must be empty."); + this.map = map; + } + + public boolean add(E e) + { + return map.put(e, true) == null; + } + + public boolean addAll(Collection<? extends E> c) + { + boolean result = false; + final Iterator<? extends E> it = c.iterator(); + while (it.hasNext()) + result |= (map.put(it.next(), true) == null); + return result; + } + + public void clear() + { + map.clear(); + } + + public boolean contains(Object o) + { + return map.containsKey(o); + } + + public boolean isEmpty() + { + return map.isEmpty(); + } + + public Iterator<E> iterator() + { + return map.keySet().iterator(); + } + + public boolean remove(Object o) + { + return map.remove(o) != null; + } + + public int size() + { + return map.size(); + } + } // class MapSet + } // class Collections diff --git a/libjava/classpath/java/util/Currency.java b/libjava/classpath/java/util/Currency.java index 32ea7534735..a0933eca2f6 100644 --- a/libjava/classpath/java/util/Currency.java +++ b/libjava/classpath/java/util/Currency.java @@ -44,6 +44,8 @@ import java.io.IOException; import java.io.ObjectStreamException; import java.io.Serializable; +import java.util.spi.CurrencyNameProvider; + /** * Representation of a currency for a particular locale. Each currency * is identified by its ISO 4217 code, and only one instance of this @@ -402,8 +404,35 @@ public final class Currency */ public String getSymbol(Locale locale) { - return LocaleHelper.getLocalizedString(locale, currencyCode, - "currenciesSymbol", false, true); + String property = "currenciesSymbol." + currencyCode; + try + { + return ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + locale).getString(property); + } + catch (MissingResourceException exception) + { + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + for (CurrencyNameProvider p : + ServiceLoader.load(CurrencyNameProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + String localizedString = p.getSymbol(currencyCode, + locale); + if (localizedString != null) + return localizedString; + break; + } + } + } + if (locale.equals(Locale.ROOT)) // Base case + return currencyCode; + return getSymbol(LocaleHelper.getFallbackLocale(locale)); } /** diff --git a/libjava/classpath/java/util/Date.java b/libjava/classpath/java/util/Date.java index f481753db8d..1ad128ebfc5 100644 --- a/libjava/classpath/java/util/Date.java +++ b/libjava/classpath/java/util/Date.java @@ -856,7 +856,7 @@ public class Date hour += 12; } else if (parseDayOfWeek(tok)) - ; // Ignore it; throw the token away. + { /* Ignore it; throw the token away. */ } else if (tok.equals("UT") || tok.equals("UTC") || tok.equals("GMT")) localTimezone = false; else if (tok.startsWith("UT") || tok.startsWith("GMT")) diff --git a/libjava/classpath/java/util/GregorianCalendar.java b/libjava/classpath/java/util/GregorianCalendar.java index 83ac00e77e0..6eb7ce84eab 100644 --- a/libjava/classpath/java/util/GregorianCalendar.java +++ b/libjava/classpath/java/util/GregorianCalendar.java @@ -1,5 +1,5 @@ /* java.util.GregorianCalendar - Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004 + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -223,7 +223,6 @@ public class GregorianCalendar extends Calendar { this(zone, locale, false); setTimeInMillis(System.currentTimeMillis()); - complete(); } /** @@ -842,13 +841,24 @@ public class GregorianCalendar extends Calendar // which day of the week are we (0..6), relative to getFirstDayOfWeek int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7; - fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH] - relativeWeekday + 12) / 7; + // which day of the week is the first of this month? + // nb 35 is the smallest multiple of 7 that ensures that + // the left hand side of the modulo operator is positive. + int relativeWeekdayOfFirst = (relativeWeekday - fields[DAY_OF_MONTH] + + 1 + 35) % 7; + + // which week of the month is the first of this month in? + int minDays = getMinimalDaysInFirstWeek(); + int weekOfFirst = ((7 - relativeWeekdayOfFirst) >= minDays) ? 1 : 0; + + // which week of the month is this day in? + fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH] + + relativeWeekdayOfFirst - 1) / 7 + weekOfFirst; int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7; // Do the Correction: getMinimalDaysInFirstWeek() is always in the // first week. - int minDays = getMinimalDaysInFirstWeek(); int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays) - getFirstDayOfWeek()) % 7; if (minDays - firstWeekday < 1) diff --git a/libjava/classpath/java/util/HashMap.java b/libjava/classpath/java/util/HashMap.java index 92022a7d55e..eca3ad6aaf3 100644 --- a/libjava/classpath/java/util/HashMap.java +++ b/libjava/classpath/java/util/HashMap.java @@ -380,11 +380,11 @@ public class HashMap<K, V> extends AbstractMap<K, V> */ public void putAll(Map<? extends K, ? extends V> m) { - Map<K,V> addMap; - - addMap = (Map<K,V>) m; - for (Map.Entry<K,V> e : addMap.entrySet()) + final Map<K,V> addMap = (Map<K,V>) m; + final Iterator<Map.Entry<K,V>> it = addMap.entrySet().iterator(); + while (it.hasNext()) { + final Map.Entry<K,V> e = it.next(); // Optimize in case the Entry is one of our own. if (e instanceof AbstractMap.SimpleEntry) { @@ -710,12 +710,12 @@ public class HashMap<K, V> extends AbstractMap<K, V> */ void putAllInternal(Map<? extends K, ? extends V> m) { - Map<K,V> addMap; - - addMap = (Map<K,V>) m; + final Map<K,V> addMap = (Map<K,V>) m; + final Iterator<Map.Entry<K,V>> it = addMap.entrySet().iterator(); size = 0; - for (Map.Entry<K,V> e : addMap.entrySet()) + while (it.hasNext()) { + final Map.Entry<K,V> e = it.next(); size++; K key = e.getKey(); int idx = hash(key); diff --git a/libjava/classpath/java/util/Hashtable.java b/libjava/classpath/java/util/Hashtable.java index 2e265a47387..a85674637e7 100644 --- a/libjava/classpath/java/util/Hashtable.java +++ b/libjava/classpath/java/util/Hashtable.java @@ -505,12 +505,11 @@ public class Hashtable<K, V> extends Dictionary<K, V> */ public synchronized void putAll(Map<? extends K, ? extends V> m) { - Map<K,V> addMap; - - addMap = (Map<K,V>) m; - - for (Map.Entry<K,V> e : addMap.entrySet()) + final Map<K,V> addMap = (Map<K,V>) m; + final Iterator<Map.Entry<K,V>> it = addMap.entrySet().iterator(); + while (it.hasNext()) { + final Map.Entry<K,V> e = it.next(); // Optimize in case the Entry is one of our own. if (e instanceof AbstractMap.SimpleEntry) { @@ -857,13 +856,12 @@ public class Hashtable<K, V> extends Dictionary<K, V> */ void putAllInternal(Map<? extends K, ? extends V> m) { - Map<K,V> addMap; - - addMap = (Map<K,V>) m; + final Map<K,V> addMap = (Map<K,V>) m; + final Iterator<Map.Entry<K,V>> it = addMap.entrySet().iterator(); size = 0; - - for (Map.Entry<K,V> e : addMap.entrySet()) + while (it.hasNext()) { + final Map.Entry<K,V> e = it.next(); size++; K key = e.getKey(); int idx = hash(key); diff --git a/libjava/classpath/java/util/LinkedList.java b/libjava/classpath/java/util/LinkedList.java index 2d78573d08c..3f8b2ad60a0 100644 --- a/libjava/classpath/java/util/LinkedList.java +++ b/libjava/classpath/java/util/LinkedList.java @@ -1,5 +1,5 @@ /* LinkedList.java -- Linked list implementation of the List interface - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -64,15 +64,17 @@ import java.lang.reflect.Array; * @author Original author unknown * @author Bryce McKinlay * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) * @see List * @see ArrayList * @see Vector * @see Collections#synchronizedList(List) * @since 1.2 - * @status missing javadoc, but complete to 1.4 + * @status Complete to 1.6 */ public class LinkedList<T> extends AbstractSequentialList<T> - implements List<T>, Queue<T>, Cloneable, Serializable + implements List<T>, Deque<T>, Cloneable, Serializable { /** * Compatible with JDK 1.2. @@ -708,6 +710,10 @@ public class LinkedList<T> extends AbstractSequentialList<T> } /** + * Adds the specified element to the end of the list. + * + * @param value the value to add. + * @return true. * @since 1.5 */ public boolean offer(T value) @@ -716,6 +722,11 @@ public class LinkedList<T> extends AbstractSequentialList<T> } /** + * Returns the first element of the list without removing + * it. + * + * @return the first element of the list. + * @throws NoSuchElementException if the list is empty. * @since 1.5 */ public T element() @@ -724,6 +735,11 @@ public class LinkedList<T> extends AbstractSequentialList<T> } /** + * Returns the first element of the list without removing + * it. + * + * @return the first element of the list, or <code>null</code> + * if the list is empty. * @since 1.5 */ public T peek() @@ -734,6 +750,10 @@ public class LinkedList<T> extends AbstractSequentialList<T> } /** + * Removes and returns the first element of the list. + * + * @return the first element of the list, or <code>null</code> + * if the list is empty. * @since 1.5 */ public T poll() @@ -744,6 +764,10 @@ public class LinkedList<T> extends AbstractSequentialList<T> } /** + * Removes and returns the first element of the list. + * + * @return the first element of the list. + * @throws NoSuchElementException if the list is empty. * @since 1.5 */ public T remove() @@ -992,4 +1016,245 @@ public class LinkedList<T> extends AbstractSequentialList<T> lastReturned.data = o; } } // class LinkedListItr + + /** + * Obtain an Iterator over this list in reverse sequential order. + * + * @return an Iterator over the elements of the list in + * reverse order. + * @since 1.6 + */ + public Iterator<T> descendingIterator() + { + return new Iterator<T>() + { + /** Number of modifications we know about. */ + private int knownMod = modCount; + + /** Entry that will be returned by next(). */ + private Entry<T> next = last; + + /** Entry that will be affected by remove() or set(). */ + private Entry<T> lastReturned; + + /** Index of `next'. */ + private int position = size() - 1; + + // This will get inlined, since it is private. + /** + * Checks for modifications made to the list from + * elsewhere while iteration is in progress. + * + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + private void checkMod() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + } + + /** + * Tests to see if there are any more objects to + * return. + * + * @return true if the start of the list has not yet been + * reached. + */ + public boolean hasNext() + { + return next != null; + } + + /** + * Retrieves the next object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are + * no more objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public T next() + { + checkMod(); + if (next == null) + throw new NoSuchElementException(); + --position; + lastReturned = next; + next = lastReturned.previous; + return lastReturned.data; + } + + /** + * Removes the last object retrieved by <code>next()</code> + * from the list, if the list supports object removal. + * + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + */ + public void remove() + { + checkMod(); + if (lastReturned == null) + throw new IllegalStateException(); + removeEntry(lastReturned); + lastReturned = null; + ++knownMod; + } + }; + } + + /** + * Inserts the specified element at the front of the list. + * + * @param value the element to insert. + * @return true. + * @since 1.6 + */ + public boolean offerFirst(T value) + { + addFirst(value); + return true; + } + + /** + * Inserts the specified element at the end of the list. + * + * @param value the element to insert. + * @return true. + * @since 1.6 + */ + public boolean offerLast(T value) + { + return add(value); + } + + /** + * Returns the first element of the list without removing + * it. + * + * @return the first element of the list, or <code>null</code> + * if the list is empty. + * @since 1.6 + */ + public T peekFirst() + { + return peek(); + } + + /** + * Returns the last element of the list without removing + * it. + * + * @return the last element of the list, or <code>null</code> + * if the list is empty. + * @since 1.6 + */ + public T peekLast() + { + if (size == 0) + return null; + return getLast(); + } + + /** + * Removes and returns the first element of the list. + * + * @return the first element of the list, or <code>null</code> + * if the list is empty. + * @since 1.6 + */ + public T pollFirst() + { + return poll(); + } + + /** + * Removes and returns the last element of the list. + * + * @return the last element of the list, or <code>null</code> + * if the list is empty. + * @since 1.6 + */ + public T pollLast() + { + if (size == 0) + return null; + return removeLast(); + } + + /** + * Pops an element from the stack by removing and returning + * the first element in the list. This is equivalent to + * calling {@link #removeFirst()}. + * + * @return the top of the stack, which is the first element + * of the list. + * @throws NoSuchElementException if the list is empty. + * @since 1.6 + * @see #removeFirst() + */ + public T pop() + { + return removeFirst(); + } + + /** + * Pushes an element on to the stack by adding it to the + * front of the list. This is equivalent to calling + * {@link #addFirst(T)}. + * + * @param value the element to push on to the stack. + * @throws NoSuchElementException if the list is empty. + * @since 1.6 + * @see #addFirst(T) + */ + public void push(T value) + { + addFirst(value); + } + + /** + * Removes the first occurrence of the specified element + * from the list, when traversing the list from head to + * tail. The list is unchanged if the element is not found. + * This is equivalent to calling {@link #remove(Object)}. + * + * @param o the element to remove. + * @return true if an instance of the object was removed. + * @since 1.6 + */ + public boolean removeFirstOccurrence(Object o) + { + return remove(o); + } + + /** + * Removes the last occurrence of the specified element + * from the list, when traversing the list from head to + * tail. The list is unchanged if the element is not found. + * + * @param o the element to remove. + * @return true if an instance of the object was removed. + * @since 1.6 + */ + public boolean removeLastOccurrence(Object o) + { + Entry<T> e = last; + while (e != null) + { + if (equals(o, e.data)) + { + removeEntry(e); + return true; + } + e = e.previous; + } + return false; + } + } diff --git a/libjava/classpath/java/util/Locale.java b/libjava/classpath/java/util/Locale.java index 4c91eeb0a48..846ae7baadc 100644 --- a/libjava/classpath/java/util/Locale.java +++ b/libjava/classpath/java/util/Locale.java @@ -46,6 +46,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.spi.LocaleNameProvider; + /** * Locales represent a specific country and culture. Classes which can be * passed a Locale object tailor their information for a given locale. For @@ -161,6 +163,11 @@ public final class Locale implements Serializable, Cloneable /** Locale which represents the French speaking portion of Canada. */ public static final Locale CANADA_FRENCH = getLocale("fr", "CA"); + /** The root locale, used as the base case in lookups by + * locale-sensitive operations. + */ + public static final Locale ROOT = new Locale("","",""); + /** * Compatible with JDK 1.1+. */ @@ -674,6 +681,8 @@ public final class Locale implements Serializable, Cloneable */ public String getDisplayLanguage(Locale inLocale) { + if (language.isEmpty()) + return ""; try { ResourceBundle res = @@ -685,8 +694,27 @@ public final class Locale implements Serializable, Cloneable } catch (MissingResourceException e) { - return language; + /* This means runtime support for the locale + * is not available, so we check providers. */ } + for (LocaleNameProvider p : + ServiceLoader.load(LocaleNameProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(inLocale)) + { + String locLang = p.getDisplayLanguage(language, + inLocale); + if (locLang != null) + return locLang; + break; + } + } + } + if (inLocale.equals(Locale.ROOT)) // Base case + return language; + return getDisplayLanguage(LocaleHelper.getFallbackLocale(inLocale)); } /** @@ -732,6 +760,8 @@ public final class Locale implements Serializable, Cloneable */ public String getDisplayCountry(Locale inLocale) { + if (country.isEmpty()) + return ""; try { ResourceBundle res = @@ -743,8 +773,27 @@ public final class Locale implements Serializable, Cloneable } catch (MissingResourceException e) { - return country; + /* This means runtime support for the locale + * is not available, so we check providers. */ } + for (LocaleNameProvider p : + ServiceLoader.load(LocaleNameProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(inLocale)) + { + String locCountry = p.getDisplayCountry(country, + inLocale); + if (locCountry != null) + return locCountry; + break; + } + } + } + if (inLocale.equals(Locale.ROOT)) // Base case + return country; + return getDisplayCountry(LocaleHelper.getFallbackLocale(inLocale)); } /** @@ -791,6 +840,8 @@ public final class Locale implements Serializable, Cloneable */ public String getDisplayVariant(Locale inLocale) { + if (variant.isEmpty()) + return ""; try { ResourceBundle res = @@ -802,8 +853,27 @@ public final class Locale implements Serializable, Cloneable } catch (MissingResourceException e) { - return variant; + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + for (LocaleNameProvider p : + ServiceLoader.load(LocaleNameProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(inLocale)) + { + String locVar = p.getDisplayVariant(variant, + inLocale); + if (locVar != null) + return locVar; + break; + } + } } + if (inLocale.equals(Locale.ROOT)) // Base case + return country; + return getDisplayVariant(LocaleHelper.getFallbackLocale(inLocale)); } /** diff --git a/libjava/classpath/java/util/PriorityQueue.java b/libjava/classpath/java/util/PriorityQueue.java index c9cfd8b0fba..9e738d6e99c 100644 --- a/libjava/classpath/java/util/PriorityQueue.java +++ b/libjava/classpath/java/util/PriorityQueue.java @@ -164,6 +164,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable { while (storage[++index] == null) ; + ++count; return storage[index]; } diff --git a/libjava/classpath/java/util/ServiceConfigurationError.java b/libjava/classpath/java/util/ServiceConfigurationError.java new file mode 100644 index 00000000000..1d006c8de20 --- /dev/null +++ b/libjava/classpath/java/util/ServiceConfigurationError.java @@ -0,0 +1,94 @@ +/* ServiceConfigurationError.java -- An error on service loading. + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +/** + * <p> + * An error thrown when a problem occurs during the loading + * of a service provider by a {@link ServiceLoader}. Such + * an error can occur for a number of reasons: + * </p> + * <ul> + * <li>An I/O error occurs</li> + * <li>The configuration file doesn't meet the specifications</li> + * <li>A listed class can not be found</li> + * <li>A listed class does not implement the service</li> + * <li>A listed class can not be instantiated</li> + * </ul> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public class ServiceConfigurationError + extends Error +{ + + /** + * Compatible with JDK 1.6 + */ + private static final long serialVersionUID = 74132770414881L; + + /** + * Constructs a new {@link ServiceConfigurationError} + * with the specified message. + * + * @param message a message describing the error, or + * <code>null</code> if none is required. + */ + public ServiceConfigurationError(String message) + { + super(message); + } + + /** + * Constructs a new {@link ServiceConfigurationError} + * with the specified message and cause. + * + * @param message a message describing the error, or + * <code>null</code> if none is required. + * @param cause the cause of the error, or + * <code>null</code> if this is unknown + * or inappropriate. + */ + public ServiceConfigurationError(String message, + Throwable cause) + { + super(message,cause); + } + +} diff --git a/libjava/classpath/java/util/ServiceLoader.java b/libjava/classpath/java/util/ServiceLoader.java new file mode 100644 index 00000000000..9935416359e --- /dev/null +++ b/libjava/classpath/java/util/ServiceLoader.java @@ -0,0 +1,274 @@ +/* ServiceLoader.java -- Allows loading of plug-in services. + Copyright (C) 2006, 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +import gnu.classpath.ServiceFactory; + +/** + * <p> + * Facilities for loading service providers. A service is + * defined by a set of interfaces or abstract classes, and + * a service provider gives a concrete implementation of this. + * Service providers may be installed as part of the runtime + * environment using JAR files in the extension directories, + * or may be simply supplied on the classpath. + * </p> + * <p> + * In terms of loading a service, the service is defined by + * a single interface or abstract class which the provider + * implements. This may not constitute the entire service, + * but is simply a mechanism by which a provider of the + * service can be loaded and its capabilities determined. + * The variety of possible services means that no more + * requirements are made of the service provider other than + * that it must have an accessible zero argument constructor + * in order to allow an instance to be created. + * </p> + * <p> + * Service providers are listed in a file named after the + * service type in the directory <code>META-INF/services</code>. + * The file contains a list of classes, and must be encoded + * using UTF-8. Whitespace is ignored. Comments can be + * included by using a <code>'#'</code> prefix; anything occurring + * on the same line after this symbol is ignored. Duplicate classes + * are ignored. + * </p> + * <p> + * The classes are loaded using the same classloader that was + * queried in order to locate the configuration file. As a result, + * the providers do not need to reside in the same JAR file as the + * resource; they merely have to be accessible to this classloader, + * which may differ from the one that loaded the file itself. + * </p> + * <p> + * Providers are located and instantiated lazily, as calls to the + * {@link #iterator()} are made. Providers are cached, and those in + * the cache are returned first. The cache may be cleared by calling + * {@link #reload()}. Service loaders always execute in the security + * context of the caller, so ideally calls should be made from a trusted + * source. + * </p> + * <p> + * Note that this class is not thread-safe, and that strange errors may + * occur as the result of the use of remote URLs occurring on the classpath, + * which lead to erroneous web pages. + * </p> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public final class ServiceLoader<S> + implements Iterable<S> +{ + + /** + * The class of the service provider. + */ + private Class<S> spi; + + /** + * The class loader for the service provider. + */ + private ClassLoader loader; + + /** + * The cache of service providers. + */ + private List<S> cache; + + /** + * The {@link gnu.classpath.ServiceFactory} iterator + * from which providers are obtained. + */ + private Iterator<S> serviceIt; + + /** + * Constructs a new {@link ServiceLoader} with + * the specified provider and class loader. + * + * @param spi the service to load. + * @param loader the class loader to use. + */ + private ServiceLoader(Class<S> spi, ClassLoader loader) + { + this.spi = spi; + this.loader = loader; + cache = new ArrayList<S>(); + } + + /** + * Lazily loads the available providers. The iterator first returns + * providers from the cache, in instantiation order, followed by any + * remaining providers, which are added to the cache after loading. + * The actual loading and parsing of the configuration file takes + * place in the {@link Iterator#hasNext()} and {@link Iterator#next()} + * methods, which means that they may result in a + * {@link ServiceConfigurationError} being thrown. If such an error + * does occur, subsequent invocations will attempt to recover. + * The {@link remove()} method is not supported and instead throws + * an {@link UnsupportedOperationException}. + * + * @return an iterator that lazily loads service providers. + */ + public Iterator<S> iterator() + { + return new Iterator<S>() + { + /** + * The cache iterator. + */ + private Iterator<S> cacheIt = cache.iterator(); + + public boolean hasNext() + { + if (cacheIt.hasNext()) + return true; + if (serviceIt == null) + serviceIt = + ServiceFactory.lookupProviders(spi, loader, true); + return serviceIt.hasNext(); + } + + public S next() + { + if (cacheIt.hasNext()) + return cacheIt.next(); + if (serviceIt == null) + serviceIt = + ServiceFactory.lookupProviders(spi, loader, true); + S nextService = serviceIt.next(); + cache.add(nextService); + return nextService; + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Creates a new service loader for the given service, + * using the context class loader of the current thread. + * This is equivalent to calling <code>ServiceLoader.load(service, + * Thread.currentThread().getContextClassLoader())</code>. + * + * @param service the interface or abstract class that represents + * the service. + * @return a new {@link ServiceLoader} instance. + */ + public static <S> ServiceLoader<S> load(Class<S> service) + { + return load(service, + Thread.currentThread().getContextClassLoader()); + } + + /** + * Creates a new service loader for the given service, + * using the specified class loader. The class loader is + * used to access the configuration file and the service + * provider instances themselves. If the loader is + * <code>null</code>, the system class loader (or, if + * this is also <code>null</code>, the bootstrap class + * loader). + * + * @param service the interface or abstract class that represents + * the service. + * @param loader the class loader used to load the configuration + * file and service providers. + * @return a new {@link ServiceLoader} instance. + */ + public static <S> ServiceLoader<S> load(Class<S> service, + ClassLoader loader) + { + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + return new ServiceLoader(service, loader); + } + + /** + * Creates a new service loader for the given service, + * using the extension class loader. If the extension + * class loader can not be found, the system class loader + * is used (or, if this is <code>null</code>, the + * bootstrap class loader). The primary use of this method + * is to only obtain installed services, ignoring any which + * may appear on the classpath. This is equivalent to calling + * <code>load(service, extClassLoader)</code> where + * <code>extClassLoader</code> is the extension class loader + * (or <code>null</code> if this is unavailable). + * + * @param service the interface or abstract class that represents + * the service. + * @return a new {@link ServiceLoader} instance. + */ + public static <S> ServiceLoader<S> loadInstalled(Class<S> service) + { + /* We expect the extension class loader to be the parent + * of the system class loader, as in + * ClassLoader.getDefaultSystemClassLoader() */ + return load(service, + ClassLoader.getSystemClassLoader().getParent()); + } + + /** + * Clears the cache of the provider, so that all providers + * are again read from the configuration file and instantiated. + */ + public void reload() + { + cache.clear(); + } + + /** + * Returns a textual representation of this + * {@link ServiceLoader}. + * + * @return a textual representation of the + * service loader. + */ + public String toString() + { + return getClass().getName() + + "[spi=" + spi + + ",loader=" + loader + + "]"; + } + +} diff --git a/libjava/classpath/java/util/StringTokenizer.java b/libjava/classpath/java/util/StringTokenizer.java index 0b59abe2fda..b230c73d869 100644 --- a/libjava/classpath/java/util/StringTokenizer.java +++ b/libjava/classpath/java/util/StringTokenizer.java @@ -181,13 +181,15 @@ public class StringTokenizer implements Enumeration<Object> { if (retDelims) return str.substring(pos, ++pos); - while (++pos < len && delim.indexOf(str.charAt(pos)) >= 0); + while (++pos < len && delim.indexOf(str.charAt(pos)) >= 0) + ; } if (pos < len) { int start = pos; - while (++pos < len && delim.indexOf(str.charAt(pos)) < 0); - + while (++pos < len && delim.indexOf(str.charAt(pos)) < 0) + ; + return str.substring(start, pos); } throw new NoSuchElementException(); diff --git a/libjava/classpath/java/util/TreeMap.java b/libjava/classpath/java/util/TreeMap.java index 88abce10d8d..f54cbc336ec 100644 --- a/libjava/classpath/java/util/TreeMap.java +++ b/libjava/classpath/java/util/TreeMap.java @@ -1,6 +1,6 @@ /* TreeMap.java -- a class providing a basic Red-Black Tree data structure, mapping Object --> Object - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -79,6 +79,7 @@ import java.io.Serializable; * @author Jon Zeppieri * @author Bryce McKinlay * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) * @see Map * @see HashMap * @see Hashtable @@ -88,10 +89,10 @@ import java.io.Serializable; * @see Collection * @see Collections#synchronizedSortedMap(SortedMap) * @since 1.2 - * @status updated to 1.4 + * @status updated to 1.6 */ public class TreeMap<K, V> extends AbstractMap<K, V> - implements SortedMap<K, V>, Cloneable, Serializable + implements NavigableMap<K, V>, Cloneable, Serializable { // Implementation note: // A red-black tree is a binary search tree with the additional properties @@ -143,6 +144,16 @@ public class TreeMap<K, V> extends AbstractMap<K, V> private transient Set<Map.Entry<K,V>> entries; /** + * The cache for {@link #descendingMap()}. + */ + private transient NavigableMap<K,V> descendingMap; + + /** + * The cache for {@link #navigableKeySet()}. + */ + private transient NavigableSet<K> nKeys; + + /** * Counts the number of modifications this TreeMap has undergone, used * by Iterators to know when to throw ConcurrentModificationExceptions. * Package visible for use by nested classes. @@ -369,46 +380,7 @@ public class TreeMap<K, V> extends AbstractMap<K, V> if (entries == null) // Create an AbstractSet with custom implementations of those methods // that can be overriden easily and efficiently. - entries = new AbstractSet<Map.Entry<K,V>>() - { - public int size() - { - return size; - } - - public Iterator<Map.Entry<K,V>> iterator() - { - return new TreeIterator(ENTRIES); - } - - public void clear() - { - TreeMap.this.clear(); - } - - public boolean contains(Object o) - { - if (! (o instanceof Map.Entry)) - return false; - Map.Entry<K,V> me = (Map.Entry<K,V>) o; - Node<K,V> n = getNode(me.getKey()); - return n != nil && AbstractSet.equals(me.getValue(), n.value); - } - - public boolean remove(Object o) - { - if (! (o instanceof Map.Entry)) - return false; - Map.Entry<K,V> me = (Map.Entry<K,V>) o; - Node<K,V> n = getNode(me.getKey()); - if (n != nil && AbstractSet.equals(me.getValue(), n.value)) - { - removeNode(n); - return true; - } - return false; - } - }; + entries = new NavigableEntrySet(); return entries; } @@ -451,7 +423,9 @@ public class TreeMap<K, V> extends AbstractMap<K, V> * in one appear in the other. The submap will throw an * {@link IllegalArgumentException} for any attempt to access or add an * element beyond the specified cutoff. The returned map does not include - * the endpoint; if you want inclusion, pass the successor element. + * the endpoint; if you want inclusion, pass the successor element + * or call <code>headMap(toKey, true)</code>. This is equivalent to + * calling <code>headMap(toKey, false)</code>. * * @param toKey the (exclusive) cutoff point * @return a view of the map less than the cutoff @@ -462,7 +436,29 @@ public class TreeMap<K, V> extends AbstractMap<K, V> */ public SortedMap<K, V> headMap(K toKey) { - return new SubMap((K)(Object)nil, toKey); + return headMap(toKey, false); + } + + /** + * Returns a view of this Map including all entries with keys less than + * (or equal to, if <code>inclusive</code> is true) <code>toKey</code>. + * The returned map is backed by the original, so changes in one appear + * in the other. The submap will throw an {@link IllegalArgumentException} + * for any attempt to access or add an element beyond the specified cutoff. + * + * @param toKey the cutoff point + * @param inclusive true if the cutoff point should be included. + * @return a view of the map less than (or equal to, if <code>inclusive</code> + * is true) the cutoff. + * @throws ClassCastException if <code>toKey</code> is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if toKey is null, but the comparator does not + * tolerate null elements + */ + public NavigableMap<K, V> headMap(K toKey, boolean inclusive) + { + return new SubMap((K)(Object)nil, inclusive + ? successor(getNode(toKey)).key : toKey); } /** @@ -479,37 +475,7 @@ public class TreeMap<K, V> extends AbstractMap<K, V> if (keys == null) // Create an AbstractSet with custom implementations of those methods // that can be overriden easily and efficiently. - keys = new AbstractSet<K>() - { - public int size() - { - return size; - } - - public Iterator<K> iterator() - { - return new TreeIterator(KEYS); - } - - public void clear() - { - TreeMap.this.clear(); - } - - public boolean contains(Object o) - { - return containsKey(o); - } - - public boolean remove(Object key) - { - Node<K,V> n = getNode((K) key); - if (n == nil) - return false; - removeNode(n); - return true; - } - }; + keys = new KeySet(); return keys; } @@ -648,7 +614,9 @@ public class TreeMap<K, V> extends AbstractMap<K, V> * {@link IllegalArgumentException} for any attempt to access or add an * element beyond the specified cutoffs. The returned map includes the low * endpoint but not the high; if you want to reverse this behavior on - * either end, pass in the successor element. + * either end, pass in the successor element or call + * {@link #subMap(K,boolean,K,boolean)}. This call is equivalent to + * <code>subMap(fromKey, true, toKey, false)</code>. * * @param fromKey the (inclusive) low cutoff point * @param toKey the (exclusive) high cutoff point @@ -661,7 +629,34 @@ public class TreeMap<K, V> extends AbstractMap<K, V> */ public SortedMap<K, V> subMap(K fromKey, K toKey) { - return new SubMap(fromKey, toKey); + return subMap(fromKey, true, toKey, false); + } + + /** + * Returns a view of this Map including all entries with keys greater (or + * equal to, if <code>fromInclusive</code> is true) <code>fromKey</code> and + * less than (or equal to, if <code>toInclusive</code> is true) + * <code>toKey</code>. The returned map is backed by the original, so + * changes in one appear in the other. The submap will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoffs. + * + * @param fromKey the low cutoff point + * @param fromInclusive true if the low cutoff point should be included. + * @param toKey the high cutoff point + * @param toInclusive true if the high cutoff point should be included. + * @return a view of the map for the specified range. + * @throws ClassCastException if either cutoff is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if fromKey or toKey is null, but the + * comparator does not tolerate null elements + * @throws IllegalArgumentException if fromKey is greater than toKey + */ + public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, + K toKey, boolean toInclusive) + { + return new SubMap(fromInclusive ? fromKey : successor(getNode(fromKey)).key, + toInclusive ? successor(getNode(toKey)).key : toKey); } /** @@ -671,6 +666,7 @@ public class TreeMap<K, V> extends AbstractMap<K, V> * {@link IllegalArgumentException} for any attempt to access or add an * element beyond the specified cutoff. The returned map includes the * endpoint; if you want to exclude it, pass in the successor element. + * This is equivalent to calling <code>tailMap(fromKey, true)</code>. * * @param fromKey the (inclusive) low cutoff point * @return a view of the map above the cutoff @@ -681,7 +677,29 @@ public class TreeMap<K, V> extends AbstractMap<K, V> */ public SortedMap<K, V> tailMap(K fromKey) { - return new SubMap(fromKey, (K)(Object)nil); + return tailMap(fromKey, true); + } + + /** + * Returns a view of this Map including all entries with keys greater or + * equal to <code>fromKey</code>. The returned map is backed by the + * original, so changes in one appear in the other. The submap will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. The returned map includes the + * endpoint; if you want to exclude it, pass in the successor element. + * + * @param fromKey the low cutoff point + * @param inclusive true if the cutoff point should be included. + * @return a view of the map above the cutoff + * @throws ClassCastException if <code>fromKey</code> is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if fromKey is null, but the comparator + * does not tolerate null elements + */ + public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) + { + return new SubMap(inclusive ? fromKey : successor(getNode(fromKey)).key, + (K)(Object)nil); } /** @@ -972,6 +990,21 @@ public class TreeMap<K, V> extends AbstractMap<K, V> */ final Node<K,V> highestLessThan(K key) { + return highestLessThan(key, false); + } + + /** + * Find the "highest" node which is < (or equal to, + * if <code>equal</code> is true) key. If key is nil, + * return last node. Package visible for use by nested + * classes. + * + * @param key the upper bound, exclusive + * @param equal true if the key should be returned if found. + * @return the previous node + */ + final Node<K,V> highestLessThan(K key, boolean equal) + { if (key == nil) return lastNode(); @@ -988,9 +1021,9 @@ public class TreeMap<K, V> extends AbstractMap<K, V> else if (comparison < 0) current = current.left; else // Exact match. - return predecessor(last); + return (equal ? last : predecessor(last)); } - return comparison <= 0 ? predecessor(last) : last; + return comparison < 0 ? predecessor(last) : last; } /** @@ -1084,8 +1117,8 @@ public class TreeMap<K, V> extends AbstractMap<K, V> /** * Find the "lowest" node which is >= key. If key is nil, return either - * nil or the first node, depending on the parameter first. - * Package visible for use by nested classes. + * nil or the first node, depending on the parameter first. Package visible + * for use by nested classes. * * @param key the lower bound, inclusive * @param first true to return the first element instead of nil for nil key @@ -1093,6 +1126,21 @@ public class TreeMap<K, V> extends AbstractMap<K, V> */ final Node<K,V> lowestGreaterThan(K key, boolean first) { + return lowestGreaterThan(key, first, true); + } + + /** + * Find the "lowest" node which is > (or equal to, if <code>equal</code> + * is true) key. If key is nil, return either nil or the first node, depending + * on the parameter first. Package visible for use by nested classes. + * + * @param key the lower bound, inclusive + * @param first true to return the first element instead of nil for nil key + * @param equal true if the key should be returned if found. + * @return the next node + */ + final Node<K,V> lowestGreaterThan(K key, boolean first, boolean equal) + { if (key == nil) return first ? firstNode() : nil; @@ -1109,7 +1157,7 @@ public class TreeMap<K, V> extends AbstractMap<K, V> else if (comparison < 0) current = current.left; else - return current; + return (equal ? current : successor(current)); } return comparison > 0 ? successor(last) : last; } @@ -1409,11 +1457,7 @@ public class TreeMap<K, V> extends AbstractMap<K, V> */ TreeIterator(int type) { - // FIXME gcj cannot handle this. Bug java/4695 - // this(type, firstNode(), nil); - this.type = type; - this.next = firstNode(); - this.max = nil; + this(type, firstNode(), nil); } /** @@ -1489,26 +1533,36 @@ public class TreeMap<K, V> extends AbstractMap<K, V> * * @author Eric Blake (ebb9@email.byu.edu) */ - private final class SubMap<SK extends K,SV extends V> - extends AbstractMap<SK,SV> - implements SortedMap<SK,SV> + private final class SubMap + extends AbstractMap<K,V> + implements NavigableMap<K,V> { /** * The lower range of this view, inclusive, or nil for unbounded. * Package visible for use by nested classes. */ - final SK minKey; + final K minKey; /** * The upper range of this view, exclusive, or nil for unbounded. * Package visible for use by nested classes. */ - final SK maxKey; + final K maxKey; /** * The cache for {@link #entrySet()}. */ - private Set<Map.Entry<SK,SV>> entries; + private Set<Map.Entry<K,V>> entries; + + /** + * The cache for {@link #descendingMap()}. + */ + private NavigableMap<K,V> descendingMap; + + /** + * The cache for {@link #navigableKeySet()}. + */ + private NavigableSet<K> nKeys; /** * Create a SubMap representing the elements between minKey (inclusive) @@ -1519,9 +1573,9 @@ public class TreeMap<K, V> extends AbstractMap<K, V> * @param maxKey the upper bound * @throws IllegalArgumentException if minKey > maxKey */ - SubMap(SK minKey, SK maxKey) + SubMap(K minKey, K maxKey) { - if (minKey != nil && maxKey != nil && compare((K) minKey, (K) maxKey) > 0) + if (minKey != nil && maxKey != nil && compare(minKey, maxKey) > 0) throw new IllegalArgumentException("fromKey > toKey"); this.minKey = minKey; this.maxKey = maxKey; @@ -1535,12 +1589,41 @@ public class TreeMap<K, V> extends AbstractMap<K, V> * @param key the key to check * @return true if the key is in range */ - boolean keyInRange(SK key) + boolean keyInRange(K key) { - return ((minKey == nil || compare((K) key, (K) minKey) >= 0) - && (maxKey == nil || compare((K) key, (K) maxKey) < 0)); + return ((minKey == nil || compare(key, minKey) >= 0) + && (maxKey == nil || compare(key, maxKey) < 0)); } + public Entry<K,V> ceilingEntry(K key) + { + Entry<K,V> n = TreeMap.this.ceilingEntry(key); + if (n != null && keyInRange(n.getKey())) + return n; + return null; + } + + public K ceilingKey(K key) + { + K found = TreeMap.this.ceilingKey(key); + if (keyInRange(found)) + return found; + else + return null; + } + + public NavigableSet<K> descendingKeySet() + { + return descendingMap().navigableKeySet(); + } + + public NavigableMap<K,V> descendingMap() + { + if (descendingMap == null) + descendingMap = new DescendingMap(this); + return descendingMap; + } + public void clear() { Node next = lowestGreaterThan(minKey, true); @@ -1553,14 +1636,14 @@ public class TreeMap<K, V> extends AbstractMap<K, V> } } - public Comparator<? super SK> comparator() + public Comparator<? super K> comparator() { return comparator; } public boolean containsKey(Object key) { - return keyInRange((SK) key) && TreeMap.this.containsKey(key); + return keyInRange((K) key) && TreeMap.this.containsKey(key); } public boolean containsValue(Object value) @@ -1576,150 +1659,160 @@ public class TreeMap<K, V> extends AbstractMap<K, V> return false; } - public Set<Map.Entry<SK,SV>> entrySet() + public Set<Map.Entry<K,V>> entrySet() { if (entries == null) // Create an AbstractSet with custom implementations of those methods // that can be overriden easily and efficiently. - entries = new AbstractSet<Map.Entry<SK,SV>>() - { - public int size() - { - return SubMap.this.size(); - } - - public Iterator<Map.Entry<SK,SV>> iterator() - { - Node first = lowestGreaterThan(minKey, true); - Node max = lowestGreaterThan(maxKey, false); - return new TreeIterator(ENTRIES, first, max); - } - - public void clear() - { - SubMap.this.clear(); - } - - public boolean contains(Object o) - { - if (! (o instanceof Map.Entry)) - return false; - Map.Entry<SK,SV> me = (Map.Entry<SK,SV>) o; - SK key = me.getKey(); - if (! keyInRange(key)) - return false; - Node<K,V> n = getNode((K) key); - return n != nil && AbstractSet.equals(me.getValue(), n.value); - } - - public boolean remove(Object o) - { - if (! (o instanceof Map.Entry)) - return false; - Map.Entry<SK,SV> me = (Map.Entry<SK,SV>) o; - SK key = me.getKey(); - if (! keyInRange(key)) - return false; - Node<K,V> n = getNode((K) key); - if (n != nil && AbstractSet.equals(me.getValue(), n.value)) - { - removeNode(n); - return true; - } - return false; - } - }; + entries = new SubMap.NavigableEntrySet(); return entries; } - public SK firstKey() + public Entry<K,V> firstEntry() { - Node<SK,SV> node = (Node<SK,SV>) lowestGreaterThan(minKey, true); + Node<K,V> node = lowestGreaterThan(minKey, true); if (node == nil || ! keyInRange(node.key)) + return null; + return node; + } + + public K firstKey() + { + Entry<K,V> e = firstEntry(); + if (e == null) throw new NoSuchElementException(); - return node.key; + return e.getKey(); } - public SV get(Object key) + public Entry<K,V> floorEntry(K key) { - if (keyInRange((SK) key)) - return (SV) TreeMap.this.get(key); + Entry<K,V> n = TreeMap.this.floorEntry(key); + if (n != null && keyInRange(n.getKey())) + return n; return null; } - public SortedMap<SK,SV> headMap(SK toKey) + public K floorKey(K key) { - if (! keyInRange(toKey)) - throw new IllegalArgumentException("key outside range"); - return new SubMap(minKey, toKey); + K found = TreeMap.this.floorKey(key); + if (keyInRange(found)) + return found; + else + return null; + } + + public V get(Object key) + { + if (keyInRange((K) key)) + return TreeMap.this.get(key); + return null; } - public Set<SK> keySet() + public SortedMap<K,V> headMap(K toKey) + { + return headMap(toKey, false); + } + + public NavigableMap<K,V> headMap(K toKey, boolean inclusive) + { + if (!keyInRange(toKey)) + throw new IllegalArgumentException("Key outside submap range"); + return new SubMap(minKey, (inclusive ? + successor(getNode(toKey)).key : toKey)); + } + + public Set<K> keySet() { if (this.keys == null) // Create an AbstractSet with custom implementations of those methods // that can be overriden easily and efficiently. - this.keys = new AbstractSet() - { - public int size() - { - return SubMap.this.size(); - } - - public Iterator<SK> iterator() - { - Node first = lowestGreaterThan(minKey, true); - Node max = lowestGreaterThan(maxKey, false); - return new TreeIterator(KEYS, first, max); - } + this.keys = new SubMap.KeySet(); + return this.keys; + } - public void clear() - { - SubMap.this.clear(); - } + public Entry<K,V> higherEntry(K key) + { + Entry<K,V> n = TreeMap.this.higherEntry(key); + if (n != null && keyInRange(n.getKey())) + return n; + return null; + } - public boolean contains(Object o) - { - if (! keyInRange((SK) o)) - return false; - return getNode((K) o) != nil; - } + public K higherKey(K key) + { + K found = TreeMap.this.higherKey(key); + if (keyInRange(found)) + return found; + else + return null; + } - public boolean remove(Object o) - { - if (! keyInRange((SK) o)) - return false; - Node n = getNode((K) o); - if (n != nil) - { - removeNode(n); - return true; - } - return false; - } - }; - return this.keys; + public Entry<K,V> lastEntry() + { + return lowerEntry(maxKey); } - public SK lastKey() + public K lastKey() { - Node<SK,SV> node = (Node<SK,SV>) highestLessThan(maxKey); - if (node == nil || ! keyInRange(node.key)) + Entry<K,V> e = lastEntry(); + if (e == null) throw new NoSuchElementException(); - return (SK) node.key; + return e.getKey(); + } + + public Entry<K,V> lowerEntry(K key) + { + Entry<K,V> n = TreeMap.this.lowerEntry(key); + if (n != null && keyInRange(n.getKey())) + return n; + return null; + } + + public K lowerKey(K key) + { + K found = TreeMap.this.lowerKey(key); + if (keyInRange(found)) + return found; + else + return null; + } + + public NavigableSet<K> navigableKeySet() + { + if (this.nKeys == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overriden easily and efficiently. + this.nKeys = new SubMap.NavigableKeySet(); + return this.nKeys; + } + + public Entry<K,V> pollFirstEntry() + { + Entry<K,V> e = firstEntry(); + if (e != null) + removeNode((Node<K,V>) e); + return e; } - public SV put(SK key, SV value) + public Entry<K,V> pollLastEntry() + { + Entry<K,V> e = lastEntry(); + if (e != null) + removeNode((Node<K,V>) e); + return e; + } + + public V put(K key, V value) { if (! keyInRange(key)) throw new IllegalArgumentException("Key outside range"); - return (SV) TreeMap.this.put(key, value); + return TreeMap.this.put(key, value); } - public SV remove(Object key) + public V remove(Object key) { - if (keyInRange((SK)key)) - return (SV) TreeMap.this.remove(key); + if (keyInRange((K)key)) + return TreeMap.this.remove(key); return null; } @@ -1736,21 +1829,34 @@ public class TreeMap<K, V> extends AbstractMap<K, V> return count; } - public SortedMap<SK, SV> subMap(SK fromKey, SK toKey) + public SortedMap<K,V> subMap(K fromKey, K toKey) + { + return subMap(fromKey, true, toKey, false); + } + + public NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, + K toKey, boolean toInclusive) { if (! keyInRange(fromKey) || ! keyInRange(toKey)) throw new IllegalArgumentException("key outside range"); - return new SubMap(fromKey, toKey); + return new SubMap(fromInclusive ? fromKey : successor(getNode(fromKey)).key, + toInclusive ? successor(getNode(toKey)).key : toKey); } - public SortedMap<SK, SV> tailMap(SK fromKey) + public SortedMap<K, V> tailMap(K fromKey) + { + return tailMap(fromKey, true); + } + + public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) { if (! keyInRange(fromKey)) throw new IllegalArgumentException("key outside range"); - return new SubMap(fromKey, maxKey); + return new SubMap(inclusive ? fromKey : successor(getNode(fromKey)).key, + maxKey); } - public Collection<SV> values() + public Collection<V> values() { if (this.values == null) // Create an AbstractCollection with custom implementations of those @@ -1762,7 +1868,7 @@ public class TreeMap<K, V> extends AbstractMap<K, V> return SubMap.this.size(); } - public Iterator<SV> iterator() + public Iterator<V> iterator() { Node first = lowestGreaterThan(minKey, true); Node max = lowestGreaterThan(maxKey, false); @@ -1776,5 +1882,1439 @@ public class TreeMap<K, V> extends AbstractMap<K, V> }; return this.values; } - } // class SubMap + + private class KeySet + extends AbstractSet<K> + { + public int size() + { + return SubMap.this.size(); + } + + public Iterator<K> iterator() + { + Node first = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + return new TreeIterator(KEYS, first, max); + } + + public void clear() + { + SubMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! keyInRange((K) o)) + return false; + return getNode((K) o) != nil; + } + + public boolean remove(Object o) + { + if (! keyInRange((K) o)) + return false; + Node n = getNode((K) o); + if (n != nil) + { + removeNode(n); + return true; + } + return false; + } + + } // class SubMap.KeySet + + private final class NavigableKeySet + extends KeySet + implements NavigableSet<K> + { + + public K ceiling(K k) + { + return SubMap.this.ceilingKey(k); + } + + public Comparator<? super K> comparator() + { + return comparator; + } + + public Iterator<K> descendingIterator() + { + return descendingSet().iterator(); + } + + public NavigableSet<K> descendingSet() + { + return new DescendingSet(this); + } + + public K first() + { + return SubMap.this.firstKey(); + } + + public K floor(K k) + { + return SubMap.this.floorKey(k); + } + + public SortedSet<K> headSet(K to) + { + return headSet(to, false); + } + + public NavigableSet<K> headSet(K to, boolean inclusive) + { + return SubMap.this.headMap(to, inclusive).navigableKeySet(); + } + + public K higher(K k) + { + return SubMap.this.higherKey(k); + } + + public K last() + { + return SubMap.this.lastKey(); + } + + public K lower(K k) + { + return SubMap.this.lowerKey(k); + } + + public K pollFirst() + { + return SubMap.this.pollFirstEntry().getKey(); + } + + public K pollLast() + { + return SubMap.this.pollLastEntry().getKey(); + } + + public SortedSet<K> subSet(K from, K to) + { + return subSet(from, true, to, false); + } + + public NavigableSet<K> subSet(K from, boolean fromInclusive, + K to, boolean toInclusive) + { + return SubMap.this.subMap(from, fromInclusive, + to, toInclusive).navigableKeySet(); + } + + public SortedSet<K> tailSet(K from) + { + return tailSet(from, true); + } + + public NavigableSet<K> tailSet(K from, boolean inclusive) + { + return SubMap.this.tailMap(from, inclusive).navigableKeySet(); + } + + } // class SubMap.NavigableKeySet + + /** + * Implementation of {@link #entrySet()}. + */ + private class EntrySet + extends AbstractSet<Entry<K,V>> + { + + public int size() + { + return SubMap.this.size(); + } + + public Iterator<Map.Entry<K,V>> iterator() + { + Node first = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + return new TreeIterator(ENTRIES, first, max); + } + + public void clear() + { + SubMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry<K,V> me = (Map.Entry<K,V>) o; + K key = me.getKey(); + if (! keyInRange(key)) + return false; + Node<K,V> n = getNode(key); + return n != nil && AbstractSet.equals(me.getValue(), n.value); + } + + public boolean remove(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry<K,V> me = (Map.Entry<K,V>) o; + K key = me.getKey(); + if (! keyInRange(key)) + return false; + Node<K,V> n = getNode(key); + if (n != nil && AbstractSet.equals(me.getValue(), n.value)) + { + removeNode(n); + return true; + } + return false; + } + } // class SubMap.EntrySet + + private final class NavigableEntrySet + extends EntrySet + implements NavigableSet<Entry<K,V>> + { + + public Entry<K,V> ceiling(Entry<K,V> e) + { + return SubMap.this.ceilingEntry(e.getKey()); + } + + public Comparator<? super Entry<K,V>> comparator() + { + return new Comparator<Entry<K,V>>() + { + public int compare(Entry<K,V> t1, Entry<K,V> t2) + { + return comparator.compare(t1.getKey(), t2.getKey()); + } + }; + } + + public Iterator<Entry<K,V>> descendingIterator() + { + return descendingSet().iterator(); + } + + public NavigableSet<Entry<K,V>> descendingSet() + { + return new DescendingSet(this); + } + + public Entry<K,V> first() + { + return SubMap.this.firstEntry(); + } + + public Entry<K,V> floor(Entry<K,V> e) + { + return SubMap.this.floorEntry(e.getKey()); + } + + public SortedSet<Entry<K,V>> headSet(Entry<K,V> to) + { + return headSet(to, false); + } + + public NavigableSet<Entry<K,V>> headSet(Entry<K,V> to, boolean inclusive) + { + return (NavigableSet<Entry<K,V>>) + SubMap.this.headMap(to.getKey(), inclusive).entrySet(); + } + + public Entry<K,V> higher(Entry<K,V> e) + { + return SubMap.this.higherEntry(e.getKey()); + } + + public Entry<K,V> last() + { + return SubMap.this.lastEntry(); + } + + public Entry<K,V> lower(Entry<K,V> e) + { + return SubMap.this.lowerEntry(e.getKey()); + } + + public Entry<K,V> pollFirst() + { + return SubMap.this.pollFirstEntry(); + } + + public Entry<K,V> pollLast() + { + return SubMap.this.pollLastEntry(); + } + + public SortedSet<Entry<K,V>> subSet(Entry<K,V> from, Entry<K,V> to) + { + return subSet(from, true, to, false); + } + + public NavigableSet<Entry<K,V>> subSet(Entry<K,V> from, boolean fromInclusive, + Entry<K,V> to, boolean toInclusive) + { + return (NavigableSet<Entry<K,V>>) + SubMap.this.subMap(from.getKey(), fromInclusive, + to.getKey(), toInclusive).entrySet(); + } + + public SortedSet<Entry<K,V>> tailSet(Entry<K,V> from) + { + return tailSet(from, true); + } + + public NavigableSet<Entry<K,V>> tailSet(Entry<K,V> from, boolean inclusive) + { + return (NavigableSet<Entry<K,V>>) + SubMap.this.tailMap(from.getKey(), inclusive).navigableKeySet(); + } + + } // class SubMap.NavigableEntrySet + +} // class SubMap + + /** + * Returns the entry associated with the least or lowest key + * that is greater than or equal to the specified key, or + * <code>null</code> if there is no such key. + * + * @param key the key relative to the returned entry. + * @return the entry with the least key greater than or equal + * to the given key, or <code>null</code> if there is + * no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is <code>null</code> + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public Entry<K,V> ceilingEntry(K key) + { + Node<K,V> n = lowestGreaterThan(key, false); + return (n == nil) ? null : n; + } + + /** + * Returns the the least or lowest key that is greater than + * or equal to the specified key, or <code>null</code> if + * there is no such key. + * + * @param key the key relative to the returned entry. + * @return the least key greater than or equal to the given key, + * or <code>null</code> if there is no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is <code>null</code> + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public K ceilingKey(K key) + { + Entry<K,V> e = ceilingEntry(key); + return (e == null) ? null : e.getKey(); + } + + /** + * Returns a reverse ordered {@link NavigableSet} view of this + * map's keys. The set is backed by the {@link TreeMap}, so changes + * in one show up in the other. The set supports element removal, + * but not element addition. + * + * @return a reverse ordered set view of the keys. + * @since 1.6 + * @see #descendingMap() + */ + public NavigableSet<K> descendingKeySet() + { + return descendingMap().navigableKeySet(); + } + + /** + * Returns a view of the map in reverse order. The descending map + * is backed by the original map, so that changes affect both maps. + * Any changes occurring to either map while an iteration is taking + * place (with the exception of a {@link Iterator#remove()} operation) + * result in undefined behaviour from the iteration. The ordering + * of the descending map is the same as for a map with a + * {@link Comparator} given by {@link Collections#reverseOrder()}, + * and calling {@link #descendingMap()} on the descending map itself + * results in a view equivalent to the original map. + * + * @return a reverse order view of the map. + * @since 1.6 + */ + public NavigableMap<K,V> descendingMap() + { + if (descendingMap == null) + descendingMap = new DescendingMap<K,V>(this); + return descendingMap; + } + + /** + * Returns the entry associated with the least or lowest key + * in the map, or <code>null</code> if the map is empty. + * + * @return the lowest entry, or <code>null</code> if the map + * is empty. + * @since 1.6 + */ + public Entry<K,V> firstEntry() + { + Node<K,V> n = firstNode(); + return (n == nil) ? null : n; + } + + /** + * Returns the entry associated with the greatest or highest key + * that is less than or equal to the specified key, or + * <code>null</code> if there is no such key. + * + * @param key the key relative to the returned entry. + * @return the entry with the greatest key less than or equal + * to the given key, or <code>null</code> if there is + * no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is <code>null</code> + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public Entry<K,V> floorEntry(K key) + { + Node<K,V> n = highestLessThan(key, true); + return (n == nil) ? null : n; + } + + /** + * Returns the the greatest or highest key that is less than + * or equal to the specified key, or <code>null</code> if + * there is no such key. + * + * @param key the key relative to the returned entry. + * @return the greatest key less than or equal to the given key, + * or <code>null</code> if there is no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is <code>null</code> + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public K floorKey(K key) + { + Entry<K,V> e = floorEntry(key); + return (e == null) ? null : e.getKey(); + } + + /** + * Returns the entry associated with the least or lowest key + * that is strictly greater than the specified key, or + * <code>null</code> if there is no such key. + * + * @param key the key relative to the returned entry. + * @return the entry with the least key greater than + * the given key, or <code>null</code> if there is + * no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is <code>null</code> + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public Entry<K,V> higherEntry(K key) + { + Node<K,V> n = lowestGreaterThan(key, false, false); + return (n == nil) ? null : n; + } + + /** + * Returns the the least or lowest key that is strictly + * greater than the specified key, or <code>null</code> if + * there is no such key. + * + * @param key the key relative to the returned entry. + * @return the least key greater than the given key, + * or <code>null</code> if there is no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is <code>null</code> + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public K higherKey(K key) + { + Entry<K,V> e = higherEntry(key); + return (e == null) ? null : e.getKey(); + } + + /** + * Returns the entry associated with the greatest or highest key + * in the map, or <code>null</code> if the map is empty. + * + * @return the highest entry, or <code>null</code> if the map + * is empty. + * @since 1.6 + */ + public Entry<K,V> lastEntry() + { + Node<K,V> n = lastNode(); + return (n == nil) ? null : n; + } + + /** + * Returns the entry associated with the greatest or highest key + * that is strictly less than the specified key, or + * <code>null</code> if there is no such key. + * + * @param key the key relative to the returned entry. + * @return the entry with the greatest key less than + * the given key, or <code>null</code> if there is + * no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is <code>null</code> + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public Entry<K,V> lowerEntry(K key) + { + Node<K,V> n = highestLessThan(key); + return (n == nil) ? null : n; + } + + /** + * Returns the the greatest or highest key that is strictly + * less than the specified key, or <code>null</code> if + * there is no such key. + * + * @param key the key relative to the returned entry. + * @return the greatest key less than the given key, + * or <code>null</code> if there is no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is <code>null</code> + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public K lowerKey(K key) + { + Entry<K,V> e = lowerEntry(key); + return (e == null) ? null : e.getKey(); + } + + /** + * Returns a {@link NavigableSet} view of this map's keys. The set is + * backed by the {@link TreeMap}, so changes in one show up in the other. + * Any changes occurring to either while an iteration is taking + * place (with the exception of a {@link Iterator#remove()} operation) + * result in undefined behaviour from the iteration. The ordering + * The set supports element removal, but not element addition. + * + * @return a {@link NavigableSet} view of the keys. + * @since 1.6 + */ + public NavigableSet<K> navigableKeySet() + { + if (nKeys == null) + nKeys = new NavigableKeySet(); + return nKeys; + } + + /** + * Removes and returns the entry associated with the least + * or lowest key in the map, or <code>null</code> if the map + * is empty. + * + * @return the removed first entry, or <code>null</code> if the + * map is empty. + * @since 1.6 + */ + public Entry<K,V> pollFirstEntry() + { + Entry<K,V> e = firstEntry(); + if (e != null) + removeNode((Node<K,V>)e); + return e; + } + + /** + * Removes and returns the entry associated with the greatest + * or highest key in the map, or <code>null</code> if the map + * is empty. + * + * @return the removed last entry, or <code>null</code> if the + * map is empty. + * @since 1.6 + */ + public Entry<K,V> pollLastEntry() + { + Entry<K,V> e = lastEntry(); + if (e != null) + removeNode((Node<K,V>)e); + return e; + } + + /** + * Implementation of {@link #descendingMap()} and associated + * derivatives. This class provides a view of the + * original backing map in reverse order, and throws + * {@link IllegalArgumentException} for attempts to + * access beyond that range. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static final class DescendingMap<DK,DV> + implements NavigableMap<DK,DV> + { + + /** + * The cache for {@link #entrySet()}. + */ + private Set<Map.Entry<DK,DV>> entries; + + /** + * The cache for {@link #keySet()}. + */ + private Set<DK> keys; + + /** + * The cache for {@link #navigableKeySet()}. + */ + private NavigableSet<DK> nKeys; + + /** + * The cache for {@link #values()}. + */ + private Collection<DV> values; + + /** + * The backing {@link NavigableMap}. + */ + private NavigableMap<DK,DV> map; + + /** + * Create a {@link DescendingMap} around the specified + * map. + * + * @param map the map to wrap. + */ + public DescendingMap(NavigableMap<DK,DV> map) + { + this.map = map; + } + + public Map.Entry<DK,DV> ceilingEntry(DK key) + { + return map.floorEntry(key); + } + + public DK ceilingKey(DK key) + { + return map.floorKey(key); + } + + public void clear() + { + map.clear(); + } + + public Comparator<? super DK> comparator() + { + return Collections.reverseOrder(map.comparator()); + } + + public boolean containsKey(Object o) + { + return map.containsKey(o); + } + + public boolean containsValue(Object o) + { + return map.containsValue(o); + } + + public NavigableSet<DK> descendingKeySet() + { + return descendingMap().navigableKeySet(); + } + + public NavigableMap<DK,DV> descendingMap() + { + return map; + } + + public Set<Entry<DK,DV>> entrySet() + { + if (entries == null) + entries = + new DescendingSet<Entry<DK,DV>>((NavigableSet<Entry<DK,DV>>) + map.entrySet()); + return entries; + } + + public boolean equals(Object o) + { + return map.equals(o); + } + + public Entry<DK,DV> firstEntry() + { + return map.lastEntry(); + } + + public DK firstKey() + { + return map.lastKey(); + } + + public Entry<DK,DV> floorEntry(DK key) + { + return map.ceilingEntry(key); + } + + public DK floorKey(DK key) + { + return map.ceilingKey(key); + } + + public DV get(Object key) + { + return map.get(key); + } + + public int hashCode() + { + return map.hashCode(); + } + + public SortedMap<DK,DV> headMap(DK toKey) + { + return headMap(toKey, false); + } + + public NavigableMap<DK,DV> headMap(DK toKey, boolean inclusive) + { + return new DescendingMap(map.tailMap(toKey, inclusive)); + } + + public Entry<DK,DV> higherEntry(DK key) + { + return map.lowerEntry(key); + } + + public DK higherKey(DK key) + { + return map.lowerKey(key); + } + + public Set<DK> keySet() + { + if (keys == null) + keys = new DescendingSet<DK>(map.navigableKeySet()); + return keys; + } + + public boolean isEmpty() + { + return map.isEmpty(); + } + + public Entry<DK,DV> lastEntry() + { + return map.firstEntry(); + } + + public DK lastKey() + { + return map.firstKey(); + } + + public Entry<DK,DV> lowerEntry(DK key) + { + return map.higherEntry(key); + } + + public DK lowerKey(DK key) + { + return map.higherKey(key); + } + + public NavigableSet<DK> navigableKeySet() + { + if (nKeys == null) + nKeys = new DescendingSet<DK>(map.navigableKeySet()); + return nKeys; + } + + public Entry<DK,DV> pollFirstEntry() + { + return pollLastEntry(); + } + + public Entry<DK,DV> pollLastEntry() + { + return pollFirstEntry(); + } + + public DV put(DK key, DV value) + { + return map.put(key, value); + } + + public void putAll(Map<? extends DK, ? extends DV> m) + { + map.putAll(m); + } + + public DV remove(Object key) + { + return map.remove(key); + } + + public int size() + { + return map.size(); + } + + public SortedMap<DK,DV> subMap(DK fromKey, DK toKey) + { + return subMap(fromKey, true, toKey, false); + } + + public NavigableMap<DK,DV> subMap(DK fromKey, boolean fromInclusive, + DK toKey, boolean toInclusive) + { + return new DescendingMap(map.subMap(fromKey, fromInclusive, + toKey, toInclusive)); + } + + public SortedMap<DK,DV> tailMap(DK fromKey) + { + return tailMap(fromKey, true); + } + + public NavigableMap<DK,DV> tailMap(DK fromKey, boolean inclusive) + { + return new DescendingMap(map.headMap(fromKey, inclusive)); + } + + public String toString() + { + StringBuilder r = new StringBuilder("{"); + final Iterator<Entry<DK,DV>> it = entrySet().iterator(); + while (it.hasNext()) + { + final Entry<DK,DV> e = it.next(); + r.append(e.getKey()); + r.append('='); + r.append(e.getValue()); + r.append(", "); + } + r.replace(r.length() - 2, r.length(), "}"); + return r.toString(); + } + + public Collection<DV> values() + { + if (values == null) + // Create an AbstractCollection with custom implementations of those + // methods that can be overriden easily and efficiently. + values = new AbstractCollection() + { + public int size() + { + return size(); + } + + public Iterator<DV> iterator() + { + return new Iterator<DV>() + { + /** The last Entry returned by a next() call. */ + private Entry<DK,DV> last; + + /** The next entry that should be returned by next(). */ + private Entry<DK,DV> next = firstEntry(); + + public boolean hasNext() + { + return next != null; + } + + public DV next() + { + if (next == null) + throw new NoSuchElementException(); + last = next; + next = higherEntry(last.getKey()); + + return last.getValue(); + } + + public void remove() + { + if (last == null) + throw new IllegalStateException(); + + DescendingMap.this.remove(last.getKey()); + last = null; + } + }; + } + + public void clear() + { + clear(); + } + }; + return values; + } + + } // class DescendingMap + + /** + * Implementation of {@link #keySet()}. + */ + private class KeySet + extends AbstractSet<K> + { + + public int size() + { + return size; + } + + public Iterator<K> iterator() + { + return new TreeIterator(KEYS); + } + + public void clear() + { + TreeMap.this.clear(); + } + + public boolean contains(Object o) + { + return containsKey(o); + } + + public boolean remove(Object key) + { + Node<K,V> n = getNode((K) key); + if (n == nil) + return false; + removeNode(n); + return true; + } + } // class KeySet + + /** + * Implementation of {@link #navigableKeySet()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private final class NavigableKeySet + extends KeySet + implements NavigableSet<K> + { + + public K ceiling(K k) + { + return ceilingKey(k); + } + + public Comparator<? super K> comparator() + { + return comparator; + } + + public Iterator<K> descendingIterator() + { + return descendingSet().iterator(); + } + + public NavigableSet<K> descendingSet() + { + return new DescendingSet<K>(this); + } + + public K first() + { + return firstKey(); + } + + public K floor(K k) + { + return floorKey(k); + } + + public SortedSet<K> headSet(K to) + { + return headSet(to, false); + } + + public NavigableSet<K> headSet(K to, boolean inclusive) + { + return headMap(to, inclusive).navigableKeySet(); + } + + public K higher(K k) + { + return higherKey(k); + } + + public K last() + { + return lastKey(); + } + + public K lower(K k) + { + return lowerKey(k); + } + + public K pollFirst() + { + return pollFirstEntry().getKey(); + } + + public K pollLast() + { + return pollLastEntry().getKey(); + } + + public SortedSet<K> subSet(K from, K to) + { + return subSet(from, true, to, false); + } + + public NavigableSet<K> subSet(K from, boolean fromInclusive, + K to, boolean toInclusive) + { + return subMap(from, fromInclusive, + to, toInclusive).navigableKeySet(); + } + + public SortedSet<K> tailSet(K from) + { + return tailSet(from, true); + } + + public NavigableSet<K> tailSet(K from, boolean inclusive) + { + return tailMap(from, inclusive).navigableKeySet(); + } + + + } // class NavigableKeySet + + /** + * Implementation of {@link #descendingSet()} and associated + * derivatives. This class provides a view of the + * original backing set in reverse order, and throws + * {@link IllegalArgumentException} for attempts to + * access beyond that range. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static final class DescendingSet<D> + implements NavigableSet<D> + { + + /** + * The backing {@link NavigableSet}. + */ + private NavigableSet<D> set; + + /** + * Create a {@link DescendingSet} around the specified + * set. + * + * @param map the set to wrap. + */ + public DescendingSet(NavigableSet<D> set) + { + this.set = set; + } + + public boolean add(D e) + { + return set.add(e); + } + + public boolean addAll(Collection<? extends D> c) + { + return set.addAll(c); + } + + public D ceiling(D e) + { + return set.floor(e); + } + + public void clear() + { + set.clear(); + } + + public Comparator<? super D> comparator() + { + return Collections.reverseOrder(set.comparator()); + } + + public boolean contains(Object o) + { + return set.contains(o); + } + + public boolean containsAll(Collection<?> c) + { + return set.containsAll(c); + } + + public Iterator<D> descendingIterator() + { + return descendingSet().iterator(); + } + + public NavigableSet<D> descendingSet() + { + return set; + } + + public boolean equals(Object o) + { + return set.equals(o); + } + + public D first() + { + return set.last(); + } + + public D floor(D e) + { + return set.ceiling(e); + } + + public int hashCode() + { + return set.hashCode(); + } + + public SortedSet<D> headSet(D to) + { + return headSet(to, false); + } + + public NavigableSet<D> headSet(D to, boolean inclusive) + { + return new DescendingSet(set.tailSet(to, inclusive)); + } + + public D higher(D e) + { + return set.lower(e); + } + + public boolean isEmpty() + { + return set.isEmpty(); + } + + public Iterator<D> iterator() + { + return new Iterator<D>() + { + + /** The last element returned by a next() call. */ + private D last; + + /** The next element that should be returned by next(). */ + private D next = first(); + + public boolean hasNext() + { + return next != null; + } + + public D next() + { + if (next == null) + throw new NoSuchElementException(); + last = next; + next = higher(last); + + return last; + } + + public void remove() + { + if (last == null) + throw new IllegalStateException(); + + DescendingSet.this.remove(last); + last = null; + } + }; + } + + public D last() + { + return set.first(); + } + + public D lower(D e) + { + return set.higher(e); + } + + public D pollFirst() + { + return set.pollLast(); + } + + public D pollLast() + { + return set.pollFirst(); + } + + public boolean remove(Object o) + { + return set.remove(o); + } + + public boolean removeAll(Collection<?> c) + { + return set.removeAll(c); + } + + public boolean retainAll(Collection<?> c) + { + return set.retainAll(c); + } + + public int size() + { + return set.size(); + } + + public SortedSet<D> subSet(D from, D to) + { + return subSet(from, true, to, false); + } + + public NavigableSet<D> subSet(D from, boolean fromInclusive, + D to, boolean toInclusive) + { + return new DescendingSet(set.subSet(from, fromInclusive, + to, toInclusive)); + } + + public SortedSet<D> tailSet(D from) + { + return tailSet(from, true); + } + + public NavigableSet<D> tailSet(D from, boolean inclusive) + { + return new DescendingSet(set.headSet(from, inclusive)); + } + + public Object[] toArray() + { + D[] array = (D[]) set.toArray(); + Arrays.sort(array, comparator()); + return array; + } + + public <T> T[] toArray(T[] a) + { + T[] array = set.toArray(a); + Arrays.sort(array, (Comparator<? super T>) comparator()); + return array; + } + + public String toString() + { + StringBuilder r = new StringBuilder("["); + final Iterator<D> it = iterator(); + while (it.hasNext()) + { + final D o = it.next(); + if (o == this) + r.append("<this>"); + else + r.append(o); + r.append(", "); + } + r.replace(r.length() - 2, r.length(), "]"); + return r.toString(); + } + + } // class DescendingSet + + private class EntrySet + extends AbstractSet<Entry<K,V>> + { + public int size() + { + return size; + } + + public Iterator<Map.Entry<K,V>> iterator() + { + return new TreeIterator(ENTRIES); + } + + public void clear() + { + TreeMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry<K,V> me = (Map.Entry<K,V>) o; + Node<K,V> n = getNode(me.getKey()); + return n != nil && AbstractSet.equals(me.getValue(), n.value); + } + + public boolean remove(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry<K,V> me = (Map.Entry<K,V>) o; + Node<K,V> n = getNode(me.getKey()); + if (n != nil && AbstractSet.equals(me.getValue(), n.value)) + { + removeNode(n); + return true; + } + return false; + } + } + + private final class NavigableEntrySet + extends EntrySet + implements NavigableSet<Entry<K,V>> + { + + public Entry<K,V> ceiling(Entry<K,V> e) + { + return ceilingEntry(e.getKey()); + } + + public Comparator<? super Entry<K,V>> comparator() + { + return new Comparator<Entry<K,V>>() + { + public int compare(Entry<K,V> t1, Entry<K,V> t2) + { + return comparator.compare(t1.getKey(), t2.getKey()); + } + }; + } + + public Iterator<Entry<K,V>> descendingIterator() + { + return descendingSet().iterator(); + } + + public NavigableSet<Entry<K,V>> descendingSet() + { + return new DescendingSet(this); + } + + public Entry<K,V> first() + { + return firstEntry(); + } + + public Entry<K,V> floor(Entry<K,V> e) + { + return floorEntry(e.getKey()); + } + + public SortedSet<Entry<K,V>> headSet(Entry<K,V> to) + { + return headSet(to, false); + } + + public NavigableSet<Entry<K,V>> headSet(Entry<K,V> to, boolean inclusive) + { + return (NavigableSet<Entry<K,V>>) headMap(to.getKey(), inclusive).entrySet(); + } + + public Entry<K,V> higher(Entry<K,V> e) + { + return higherEntry(e.getKey()); + } + + public Entry<K,V> last() + { + return lastEntry(); + } + + public Entry<K,V> lower(Entry<K,V> e) + { + return lowerEntry(e.getKey()); + } + + public Entry<K,V> pollFirst() + { + return pollFirstEntry(); + } + + public Entry<K,V> pollLast() + { + return pollLastEntry(); + } + + public SortedSet<Entry<K,V>> subSet(Entry<K,V> from, Entry<K,V> to) + { + return subSet(from, true, to, false); + } + + public NavigableSet<Entry<K,V>> subSet(Entry<K,V> from, boolean fromInclusive, + Entry<K,V> to, boolean toInclusive) + { + return (NavigableSet<Entry<K,V>>) subMap(from.getKey(), fromInclusive, + to.getKey(), toInclusive).entrySet(); + } + + public SortedSet<Entry<K,V>> tailSet(Entry<K,V> from) + { + return tailSet(from, true); + } + + public NavigableSet<Entry<K,V>> tailSet(Entry<K,V> from, boolean inclusive) + { + return (NavigableSet<Entry<K,V>>) tailMap(from.getKey(), inclusive).navigableKeySet(); + } + + } // class NavigableEntrySet + } // class TreeMap diff --git a/libjava/classpath/java/util/TreeSet.java b/libjava/classpath/java/util/TreeSet.java index 2851e4a5a8f..572cda6425c 100644 --- a/libjava/classpath/java/util/TreeSet.java +++ b/libjava/classpath/java/util/TreeSet.java @@ -79,10 +79,10 @@ import java.io.Serializable; * @see Collections#synchronizedSortedSet(SortedSet) * @see TreeMap * @since 1.2 - * @status updated to 1.4 + * @status updated to 1.6 */ public class TreeSet<T> extends AbstractSet<T> - implements SortedSet<T>, Cloneable, Serializable + implements NavigableSet<T>, Cloneable, Serializable { /** * Compatible with JDK 1.2. @@ -90,11 +90,11 @@ public class TreeSet<T> extends AbstractSet<T> private static final long serialVersionUID = -2479143000061671589L; /** - * The SortedMap which backs this Set. + * The NavigableMap which backs this Set. */ // Not final because of readObject. This will always be one of TreeMap or // TreeMap.SubMap, which both extend AbstractMap. - private transient SortedMap<T, String> map; + private transient NavigableMap<T, String> map; /** * Construct a new TreeSet whose backing TreeMap using the "natural" @@ -163,7 +163,7 @@ public class TreeSet<T> extends AbstractSet<T> * * @param backingMap the submap */ - private TreeSet(SortedMap<T,String> backingMap) + private TreeSet(NavigableMap<T,String> backingMap) { map = backingMap; } @@ -220,7 +220,7 @@ public class TreeSet<T> extends AbstractSet<T> { copy = (TreeSet<T>) super.clone(); // Map may be either TreeMap or TreeMap.SubMap, hence the ugly casts. - copy.map = (SortedMap<T, String>) ((AbstractMap<T, String>) map).clone(); + copy.map = (NavigableMap<T, String>) ((AbstractMap<T, String>) map).clone(); } catch (CloneNotSupportedException x) { @@ -269,7 +269,9 @@ public class TreeSet<T> extends AbstractSet<T> * in one appear in the other. The subset will throw an * {@link IllegalArgumentException} for any attempt to access or add an * element beyond the specified cutoff. The returned set does not include - * the endpoint; if you want inclusion, pass the successor element. + * the endpoint; if you want inclusion, pass the successor element or + * call {@link #headSet(T,boolean)}. This call is equivalent to + * <code>headSet(to, false)</code>. * * @param to the (exclusive) cutoff point * @return a view of the set less than the cutoff @@ -280,7 +282,28 @@ public class TreeSet<T> extends AbstractSet<T> */ public SortedSet<T> headSet(T to) { - return new TreeSet<T>(map.headMap(to)); + return headSet(to, false); + } + + /** + * Returns a view of this Set including all elements less than + * (or equal to, if <code>inclusive</code> is true) <code>to</code>. + * The returned set is backed by the original, so changes + * in one appear in the other. The subset will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. + * + * @param to the cutoff point + * @param inclusive true if <code>to</code> should be included. + * @return a view of the set for the specified range. + * @throws ClassCastException if <code>to</code> is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if to is null, but the comparator does not + * tolerate null elements + */ + public NavigableSet<T> headSet(T to, boolean inclusive) + { + return new TreeSet<T>(map.headMap(to, inclusive)); } /** @@ -345,7 +368,9 @@ public class TreeSet<T> extends AbstractSet<T> * the other. The subset will throw an {@link IllegalArgumentException} * for any attempt to access or add an element beyond the specified cutoffs. * The returned set includes the low endpoint but not the high; if you want - * to reverse this behavior on either end, pass in the successor element. + * to reverse this behavior on either end, pass in the successor element + * or call {@link #subSet(T,boolean,T,boolean)}. This is equivalent to + * calling <code>subSet(from,true,to,false)</code>. * * @param from the (inclusive) low cutoff point * @param to the (exclusive) high cutoff point @@ -358,7 +383,33 @@ public class TreeSet<T> extends AbstractSet<T> */ public SortedSet<T> subSet(T from, T to) { - return new TreeSet<T>(map.subMap(from, to)); + return subSet(from, true, to, false); + } + + /** + * Returns a view of this Set including all elements greater than (or equal to, + * if <code>fromInclusive</code> is true</code> <code>from</code> and less than + * (or equal to, if <code>toInclusive</code> is true) <code>to</code>. + * The returned set is backed by the original, so changes in one appear in + * the other. The subset will throw an {@link IllegalArgumentException} + * for any attempt to access or add an element beyond the specified cutoffs. + * + * @param from the low cutoff point + * @param fromInclusive true if <code>from</code> should be included. + * @param to the high cutoff point + * @param toInclusive true if <code>to</code> should be included. + * @return a view of the set for the specified range. + * @throws ClassCastException if either cutoff is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if from or to is null, but the comparator + * does not tolerate null elements + * @throws IllegalArgumentException if from is greater than to + */ + public NavigableSet<T> subSet(T from, boolean fromInclusive, + T to, boolean toInclusive) + { + return new TreeSet<T>(map.subMap(from, fromInclusive, + to, toInclusive)); } /** @@ -367,7 +418,9 @@ public class TreeSet<T> extends AbstractSet<T> * changes in one appear in the other. The subset will throw an * {@link IllegalArgumentException} for any attempt to access or add an * element beyond the specified cutoff. The returned set includes the - * endpoint; if you want to exclude it, pass in the successor element. + * endpoint; if you want to exclude it, pass in the successor element + * or call {@link #tailSet(T,boolean)}. This is equivalent to calling + * <code>tailSet(from, true)</code>. * * @param from the (inclusive) low cutoff point * @return a view of the set above the cutoff @@ -378,7 +431,27 @@ public class TreeSet<T> extends AbstractSet<T> */ public SortedSet<T> tailSet(T from) { - return new TreeSet<T>(map.tailMap(from)); + return tailSet(from, true); + } + + /** + * Returns a view of this Set including all elements greater (or equal to, + * if <code>inclusive</code> is true) <code>from</code>. The returned set + * is backed by the original, so changes in one appear in the other. The + * subset will throw an {@link IllegalArgumentException} for any attempt + * to access or add an element beyond the specified cutoff. + * + * @param from the low cutoff point. + * @param inclusive true if <code>from</code> should be included. + * @return a view of the set for the specified range. + * @throws ClassCastException if <code>from</code> is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if from is null, but the comparator + * does not tolerate null elements + */ + public NavigableSet<T> tailSet(T from, boolean inclusive) + { + return new TreeSet<T>(map.tailMap(from, inclusive)); } /** @@ -418,4 +491,151 @@ public class TreeSet<T> extends AbstractSet<T> map = new TreeMap<T, String>(comparator); ((TreeMap<T, String>) map).putFromObjStream(s, size, false); } + + /** + * Returns the least or lowest element in the set greater than or + * equal to the given element, or <code>null</code> if there is + * no such element. + * + * @param e the element relative to the returned element. + * @return the least element greater than or equal + * to the given element, or <code>null</code> if there is + * no such element. + * @throws ClassCastException if the specified element can not + * be compared with those in the map. + * @throws NullPointerException if the element is <code>null</code> + * and this set either uses natural + * ordering or a comparator that does + * not permit null elements. + * @since 1.6 + */ + public T ceiling(T e) + { + return map.ceilingKey(e); + } + + /** + * Returns an iterator over the elements of this set in descending + * order. This is equivalent to calling + * <code>descendingSet().iterator()</code>. + * + * @return an iterator over the elements in descending order. + * @since 1.6 + */ + public Iterator<T> descendingIterator() + { + return descendingSet().iterator(); + } + + /** + * Returns a view of the set in reverse order. The descending set + * is backed by the original set, so that changes affect both sets. + * Any changes occurring to either set while an iteration is taking + * place (with the exception of a {@link Iterator#remove()} operation) + * result in undefined behaviour from the iteration. The ordering + * of the descending set is the same as for a set with a + * {@link Comparator} given by {@link Collections#reverseOrder()}, + * and calling {@link #descendingSet()} on the descending set itself + * results in a view equivalent to the original set. + * + * @return a reverse order view of the set. + * @since 1.6 + */ + public NavigableSet<T> descendingSet() + { + return map.descendingKeySet(); + } + + /** + * Returns the greatest or highest element in the set less than or + * equal to the given element, or <code>null</code> if there is + * no such element. + * + * @param e the element relative to the returned element. + * @return the greatest element less than or equal + * to the given element, or <code>null</code> if there is + * no such element. + * @throws ClassCastException if the specified element can not + * be compared with those in the map. + * @throws NullPointerException if the element is <code>null</code> + * and this set either uses natural + * ordering or a comparator that does + * not permit null elements. + * @since 1.6 + */ + public T floor(T e) + { + return map.floorKey(e); + } + + /** + * Returns the least or lowest element in the set strictly greater + * than the given element, or <code>null</code> if there is + * no such element. + * + * @param e the element relative to the returned element. + * @return the least element greater than + * the given element, or <code>null</code> if there is + * no such element. + * @throws ClassCastException if the specified element can not + * be compared with those in the map. + * @throws NullPointerException if the element is <code>null</code> + * and this set either uses natural + * ordering or a comparator that does + * not permit null elements. + * @since 1.6 + */ + public T higher(T e) + { + return map.higherKey(e); + } + + /** + * Returns the greatest or highest element in the set strictly less + * than the given element, or <code>null</code> if there is + * no such element. + * + * @param e the element relative to the returned element. + * @return the greatest element less than + * the given element, or <code>null</code> if there is + * no such element. + * @throws ClassCastException if the specified element can not + * be compared with those in the map. + * @throws NullPointerException if the element is <code>null</code> + * and this set either uses natural + * ordering or a comparator that does + * not permit null elements. + * @since 1.6 + */ + public T lower(T e) + { + return map.lowerKey(e); + } + + /** + * Removes and returns the least or lowest element in the set, + * or <code>null</code> if the map is empty. + * + * @return the removed first element, or <code>null</code> if the + * map is empty. + * @since 1.6 + */ + public T pollFirst() + { + return map.pollFirstEntry().getKey(); + } + + /** + * Removes and returns the greatest or highest element in the set, + * or <code>null</code> if the map is empty. + * + * @return the removed last element, or <code>null</code> if the + * map is empty. + * @since 1.6 + */ + public T pollLast() + { + return map.pollLastEntry().getKey(); + } + } diff --git a/libjava/classpath/java/util/class-dependencies.conf b/libjava/classpath/java/util/class-dependencies.conf deleted file mode 100644 index 39f96062744..00000000000 --- a/libjava/classpath/java/util/class-dependencies.conf +++ /dev/null @@ -1,78 +0,0 @@ -# This property file contains dependencies of classes, methods, and -# field on other methods or classes. -# -# Syntax: -# -# <used>: <needed 1> [... <needed N>] -# -# means that when <used> is included, <needed 1> (... <needed N>) must -# be included as well. -# -# <needed X> and <used> are of the form -# -# <class.methodOrField(signature)> -# -# or just -# -# <class> -# -# Within dependencies, variables can be used. A variable is defined as -# follows: -# -# {variable}: value1 value2 ... value<n> -# -# variables can be used on the right side of dependencies as follows: -# -# <used>: com.bla.blu.{variable}.Class.m()V -# -# The use of the variable will expand to <n> dependencies of the form -# -# <used>: com.bla.blu.value1.Class.m()V -# <used>: com.bla.blu.value2.Class.m()V -# ... -# <used>: com.bla.blu.value<n>.Class.m()V -# -# Variables can be redefined when building a system to select the -# required support for features like encodings, protocols, etc. -# -# Hints: -# -# - For methods and fields, the signature is mandatory. For -# specification, please see the Java Virtual Machine Specification by -# SUN. Unlike in the spec, field signatures (types) are in brackets. -# -# - Package names must be separated by '/' (and not '.'). E.g., -# java/lang/Class (this is necessary, because the '.' is used to -# separate method or field names from classes) -# -# - In case <needed> refers to a class, only the class itself will be -# included in the resulting binary, NOT necessarily all its methods -# and fields. If you want to refer to all methods and fields, you can -# write class.* as an abbreviation. -# -# - Abbreviations for packages are also possible: my/package/* means all -# methods and fields of all classes in my/package. -# -# - A line with a trailing '\' continues in the next line. - - -# All calendars supported are loaded via java/util/Calendar.getBundle or -# java/util/GregorianCalendar.getBundle from class -# gnu/java/locale/Calendar_{locale_id} -# -# This introduces a dependency for the localized calendars. To allow an easy -# selection and addition of locales, the library variable {calendar_locales} -# can be set to the set of supported calendar locales. -# - -{calendar_locales}: de en nl - -java/util/Calendar.getBundle(Ljava/util/Locale;)Ljava/util/ResourceBundle;: \ - gnu/java/locale/Calendar.* \ - gnu/java/locale/Calendar_{calendar_locales}.* - -java/util/GregorianCalendar.getBundle(Ljava/util/Locale;)Ljava/util/ResourceBundle;: \ - gnu/java/locale/Calendar.* \ - gnu/java/locale/Calendar_{calendar_locales}.* - -# end of file diff --git a/libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java b/libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java index 5ef37d94916..48c017f50fa 100644 --- a/libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java +++ b/libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java @@ -349,7 +349,8 @@ public class CopyOnWriteArrayList<E> extends AbstractList<E> implements { E[] data = this.data; E[] newData = (E[]) new Object[data.length - 1]; - System.arraycopy(data, 0, newData, 0, index - 1); + if (index > 0) + System.arraycopy(data, 0, newData, 0, index - 1); System.arraycopy(data, index + 1, newData, index, data.length - index - 1); E r = data[index]; diff --git a/libjava/classpath/java/util/logging/LogManager.java b/libjava/classpath/java/util/logging/LogManager.java index fbc0fe78abf..6daf3dbd9eb 100644 --- a/libjava/classpath/java/util/logging/LogManager.java +++ b/libjava/classpath/java/util/logging/LogManager.java @@ -1,6 +1,6 @@ /* LogManager.java -- a class for maintaining Loggers and managing configuration properties - Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc. + Copyright (C) 2002, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -446,8 +446,8 @@ public class LogManager Iterator<WeakReference<Logger>> iter = loggers.values().iterator(); while (iter.hasNext()) - for (WeakReference<Logger> ref : loggers.values()) { + WeakReference<Logger> ref; Logger logger; ref = iter.next(); @@ -559,13 +559,21 @@ public class LogManager if ("handlers".equals(key)) { - StringTokenizer tokenizer = new StringTokenizer(value); + // In Java 5 and earlier this was specified to be + // whitespace-separated, but in reality it also accepted + // commas (tomcat relied on this), and in Java 6 the + // documentation was updated to fit the implementation. + StringTokenizer tokenizer = new StringTokenizer(value, + " \t\n\r\f,"); while (tokenizer.hasMoreTokens()) { String handlerName = tokenizer.nextToken(); Handler handler = (Handler) createInstance(handlerName, Handler.class, key); - Logger.root.addHandler(handler); + // Tomcat also relies on the implementation ignoring + // items in 'handlers' which are not class names. + if (handler != null) + Logger.root.addHandler(handler); } } diff --git a/libjava/classpath/java/util/logging/Logger.java b/libjava/classpath/java/util/logging/Logger.java index 46588e542ee..01ef8f522b8 100644 --- a/libjava/classpath/java/util/logging/Logger.java +++ b/libjava/classpath/java/util/logging/Logger.java @@ -1,5 +1,5 @@ /* Logger.java -- a class for logging messages - Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2006, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -276,8 +276,8 @@ public class Logger LogManager lm = LogManager.getLogManager(); Logger result; - /* Throw NullPointerException if name is null. */ - name.getClass(); + if (name == null) + throw new NullPointerException(); /* Without synchronized(lm), it could happen that another thread * would create a logger between our calls to getLogger and @@ -1013,8 +1013,8 @@ public class Logger public synchronized void addHandler(Handler handler) throws SecurityException { - /* Throw a new NullPointerException if handler is null. */ - handler.getClass(); + if (handler == null) + throw new NullPointerException(); /* An application is allowed to control an anonymous logger * without having the permission to control the logging @@ -1057,8 +1057,8 @@ public class Logger if (!anonymous) LogManager.getLogManager().checkAccess(); - /* Throw a new NullPointerException if handler is null. */ - handler.getClass(); + if (handler == null) + throw new NullPointerException(); handlerList.remove(handler); handlers = getHandlers(); @@ -1166,8 +1166,8 @@ public class Logger */ public synchronized void setParent(Logger parent) { - /* Throw a new NullPointerException if parent is null. */ - parent.getClass(); + if (parent == null) + throw new NullPointerException(); if (this == root) throw new IllegalArgumentException( diff --git a/libjava/classpath/java/util/prefs/AbstractPreferences.java b/libjava/classpath/java/util/prefs/AbstractPreferences.java index e676dc3105c..f3a62e6980d 100644 --- a/libjava/classpath/java/util/prefs/AbstractPreferences.java +++ b/libjava/classpath/java/util/prefs/AbstractPreferences.java @@ -45,6 +45,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.TreeSet; @@ -97,17 +98,18 @@ public abstract class AbstractPreferences extends Preferences { * accessed by earlier <code>getChild()</code> or <code>childSpi()</code> * invocations and that have not been removed. */ - private HashMap childCache = new HashMap(); + private HashMap<String, AbstractPreferences> childCache + = new HashMap<String, AbstractPreferences>(); /** * A list of all the registered NodeChangeListener objects. */ - private ArrayList nodeListeners; + private ArrayList<NodeChangeListener> nodeListeners; /** * A list of all the registered PreferenceChangeListener objects. */ - private ArrayList preferenceListeners; + private ArrayList<PreferenceChangeListener> preferenceListeners; // constructor @@ -202,7 +204,8 @@ public abstract class AbstractPreferences extends Preferences { */ protected final AbstractPreferences[] cachedChildren() { - return (AbstractPreferences[]) childCache.values().toArray(); + Collection<AbstractPreferences> vals = childCache.values(); + return vals.toArray(new AbstractPreferences[vals.size()]); } /** @@ -228,7 +231,7 @@ public abstract class AbstractPreferences extends Preferences { if (isRemoved()) throw new IllegalStateException("Node removed"); - TreeSet childrenNames = new TreeSet(); + TreeSet<String> childrenNames = new TreeSet<String>(); // First get all cached node names childrenNames.addAll(childCache.keySet()); @@ -1165,7 +1168,7 @@ public abstract class AbstractPreferences extends Preferences { if (listener == null) throw new NullPointerException("listener is null"); if (nodeListeners == null) - nodeListeners = new ArrayList(); + nodeListeners = new ArrayList<NodeChangeListener>(); nodeListeners.add(listener); } } @@ -1184,7 +1187,7 @@ public abstract class AbstractPreferences extends Preferences { if (listener == null) throw new NullPointerException("listener is null"); if (preferenceListeners == null) - preferenceListeners = new ArrayList(); + preferenceListeners = new ArrayList<PreferenceChangeListener>(); preferenceListeners.add(listener); } } diff --git a/libjava/classpath/java/util/prefs/Preferences.java b/libjava/classpath/java/util/prefs/Preferences.java index e53e4fc7938..e8cdda8ed0a 100644 --- a/libjava/classpath/java/util/prefs/Preferences.java +++ b/libjava/classpath/java/util/prefs/Preferences.java @@ -183,9 +183,9 @@ public abstract class Preferences { // Get the factory if (factory == null) { // Caller might not have enough permissions - factory = (PreferencesFactory) AccessController.doPrivileged( - new PrivilegedAction() { - public Object run() { + factory = AccessController.doPrivileged( + new PrivilegedAction<PreferencesFactory>() { + public PreferencesFactory run() { PreferencesFactory pf = null; String className = System.getProperty ("java.util.prefs.PreferencesFactory"); diff --git a/libjava/classpath/java/util/regex/Pattern.java b/libjava/classpath/java/util/regex/Pattern.java index d716fa4e62d..217ce0862a9 100644 --- a/libjava/classpath/java/util/regex/Pattern.java +++ b/libjava/classpath/java/util/regex/Pattern.java @@ -1,5 +1,5 @@ /* Pattern.java -- Compiled regular expression ready to be applied. - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -246,7 +246,7 @@ public final class Pattern implements Serializable { String t = input.subSequence(start, input.length()).toString(); if ("".equals(t) && limit == 0) - ; // Don't add. + { /* Don't add. */ } else list.add(t); } @@ -260,4 +260,14 @@ public final class Pattern implements Serializable { return regex; } + + /** + * Return the regular expression used to construct this object. + * @specnote Prior to JDK 1.5 this method had a different behavior + * @since 1.5 + */ + public String toString() + { + return regex; + } } diff --git a/libjava/classpath/java/util/spi/CurrencyNameProvider.java b/libjava/classpath/java/util/spi/CurrencyNameProvider.java new file mode 100644 index 00000000000..14fae4d87a3 --- /dev/null +++ b/libjava/classpath/java/util/spi/CurrencyNameProvider.java @@ -0,0 +1,100 @@ +/* CurrencyNameProvider.java -- Providers of localized currency symbols + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.spi; + +import java.util.Locale; + +/** + * A {@link CurrencyNameProvider} provides localized + * versions of the symbols that represent a particular + * currency. Note that currency symbols are regarded + * as names, and thus a <code>null</code> value may + * be returned, which should be treated as a lack of + * support for the specified {@link Locale}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class CurrencyNameProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link CurrencyNameProvider}. + * Provided for implicit invocation by subclasses. + */ + protected CurrencyNameProvider() + { + } + + /** + * <p> + * This method returns the symbol which precedes or follows a + * value in this particular currency. The returned value is + * the symbol used to denote the currency in the specified locale. + * </p> + * <p> + * For example, a supplied locale may specify a different symbol + * for the currency, due to conflicts with its own currency. + * This would be the case with the American currency, the dollar. + * Locales that also use a dollar-based currency (e.g. Canada, Australia) + * need to differentiate the American dollar using 'US$' rather than '$'. + * So, supplying one of these locales to <code>getSymbol()</code> would + * return this value, rather than the standard '$'. + * </p> + * <p> + * In cases where there is no such symbol for a particular currency, + * <code>null</code> should be returned. + * </p> + * + * @param currencyCode the ISO 4217 currency code, consisting + * of three uppercase letters from 'A' to 'Z' + * @param locale the locale to express the symbol in. + * @return the currency symbol, or <code>null</code> if one is + * unavailable. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the currency code is + * not in the correct format + * or the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.util.Currency#getSymbol(java.util.Locale) + */ + public abstract String getSymbol(String currencyCode, Locale locale); + +} diff --git a/libjava/classpath/java/util/spi/LocaleNameProvider.java b/libjava/classpath/java/util/spi/LocaleNameProvider.java new file mode 100644 index 00000000000..dfd2e4cbc0a --- /dev/null +++ b/libjava/classpath/java/util/spi/LocaleNameProvider.java @@ -0,0 +1,135 @@ +/* LocaleNameProvider.java -- Providers of localized locale names + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.spi; + +import java.util.Locale; + +/** + * A {@link LocaleNameProvider} provides localized + * versions of the names that represent a particular + * locale. Note that a <code>null</code> value may + * be returned, which should be treated as a lack of + * support for the specified {@link Locale}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class LocaleNameProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link LocaleNameProvider}. + * Provided for implicit invocation by subclasses. + */ + protected LocaleNameProvider() + { + } + + /** + * Returns the localized name for the specified ISO 3166 + * country in the supplied {@link java.util.Locale}. + * For example, if the country code is <code>"DE"</code>, + * this method will return <code>"Germany"</code> for + * {@link Locale.ENGLISH} but <code>"Deutschland"</code> + * for {@link Locale.GERMANY}. If the name of the country + * in the given locale is not supported, <code>null</code> + * is returned. + * + * @param countryCode the ISO 3166 country code, consisting + * of two uppercase letters from 'A' to 'Z' + * @param locale the locale to express the country in. + * @return the country name, or <code>null</code> if one is + * not available. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the country code is + * not in the correct format + * or the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.util.Locale#getDisplayCountry(java.util.Locale) + */ + public abstract String getDisplayCountry(String countryCode, + Locale locale); + + /** + * Returns the localized name for the specified ISO 639 + * language in the supplied {@link java.util.Locale}. + * For example, if the language code is <code>"de"</code>, + * this method will return <code>"German"</code> for + * {@link Locale.ENGLISH} but <code>"Deutsch"</code> + * for {@link Locale.GERMANY}. If the name of the language + * in the given locale is not supported, <code>null</code> + * is returned. + * + * @param langCode the ISO 639 language code, consisting + * of two lowercase letters from 'a' to 'z' + * @param locale the locale to express the language in. + * @return the country name, or <code>null</code> if one is + * not available. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the language code is + * not in the correct format + * or the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.util.Locale#getDisplayLanguage(java.util.Locale) + */ + public abstract String getDisplayLanguage(String langCode, + Locale locale); + + /** + * Returns the localized name for the specified variant + * in the supplied {@link java.util.Locale}. If the name + * of the variant in the given locale is not supported, + * <code>null</code> is returned. + * + * @param variant the variant. + * @param locale the locale to express the variant in. + * @return the localized variant, or <code>null</code> if one is + * not available. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.util.Locale#getDisplayVariant(java.util.Locale) + */ + public abstract String getDisplayVariant(String variant, + Locale locale); + +} diff --git a/libjava/classpath/java/util/spi/LocaleServiceProvider.java b/libjava/classpath/java/util/spi/LocaleServiceProvider.java new file mode 100644 index 00000000000..bb5b68527fc --- /dev/null +++ b/libjava/classpath/java/util/spi/LocaleServiceProvider.java @@ -0,0 +1,125 @@ +/* LocaleServiceProvider.java -- Superclass of locale SPIs + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.spi; + +import java.util.Locale; + +/** + * <p> + * This is the superclass of all the {@link Locale} service + * provider interfaces or SPIs. The locale SPIs are used + * to allow for the provision of additional support for + * locale-specific data. The runtime environment has its + * own collection of locale data, but these interfaces allow + * this to be extended by external classes. + * </p> + * <p> + * Service providers are created as concrete implementations + * of these interfaces, and accessed using the extension + * mechanism, realised by {@link ServiceLoader}. When a factory + * method of one of the locale-specific classes (such as + * {@link java.text.DateFormatSymbols} or {@link java.util.Currency}) + * is called, the runtime environment is first asked to + * provide data for the specified locale. If the runtime + * environment fails to provide this, then the offer is + * made to service providers which implement the appropriate + * interface. + * </p> + * <p> + * Each provider implements the method specified by this + * class, {@link #getAvailableLocales()}. This method is + * called first to determine whether the provider will be of + * any use in providing data for the specified locale. If + * a provider is found to be capable, then a more specific + * method appropriate to the class requiring the data will + * be called. In the case of {@link java.text.DateFormatSymbols}, + * this would be + * {@link java.text.spi.DateFormatSymbols#getInstance(Locale)}. + * </p> + * <p> + * If neither a service provider nor the runtime environment + * itself can fulfill the request, a fallback procedure is + * engaged. The locale is modified by applying the first + * applicable rule: + * </p> + * <ol> + * <li>If the variant contains a <code>'_'</code>, then + * this and everything following it is trimmed.</li> + * <li>If the variant is non-empty, it is converted to + * an empty string.</li> + * <li>If the country is non-empty, it is converted to + * an empty string.</li> + * <li>If the language is non-empty, it is converted to + * an empty string.</li> + * </ol> + * <p> + * The modified locale is then used to start the same + * process again. The root locale (@link java.util.Locale#ROOT} + * must be supported by the runtime environment in order + * to terminate this cycle. + * </p> + * <p> + * Note that any names returned by the providers may + * be <code>null</code>. Returning a <code>null</code> + * name is considered equivalent to not supporting a + * particular locale. + * </p> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class LocaleServiceProvider +{ + + /** + * Constructs a new {@link LocaleServiceProvider}. + * Provided for implicit invocation by subclasses. + */ + protected LocaleServiceProvider() + { + } + + /** + * Returns an array of {@link Locale} instances, + * for which the provider can supply localized data. + * + * @return an array of supported locales. + */ + public abstract Locale[] getAvailableLocales(); + +} diff --git a/libjava/classpath/java/util/spi/TimeZoneNameProvider.java b/libjava/classpath/java/util/spi/TimeZoneNameProvider.java new file mode 100644 index 00000000000..2815670574c --- /dev/null +++ b/libjava/classpath/java/util/spi/TimeZoneNameProvider.java @@ -0,0 +1,97 @@ +/* TimeZoneNameProvider.java -- Providers of localized currency symbols + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.spi; + +import java.util.Locale; + +/** + * A {@link TimeZoneNameProvider} provides localized + * versions of the names that represent a particular + * timezone. A <code>null</code> value may + * be returned, which should be treated as a lack of + * support for the specified {@link Locale}. The names + * from this class are also used by + * {@link DateFormatSymbols#getZoneStrings()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class TimeZoneNameProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link TimeZoneNameProvider}. + * Provided for implicit invocation by subclasses. + */ + protected TimeZoneNameProvider() + { + } + + /** + * Returns a name for the specified time zone identifier + * localized to the supplied {@link java.util.Locale}. + * The time zone identifier is either <code>"GMT"</code> + * or one of the identifiers from the public domain "tz + * database" found at <a href="ftp://elsie.nci.nih.gov/pub/"> + * ftp://elsie.nci.nih.gov/pub</a>. Note that a translated + * name for the daylight savings time variant should be returned, + * even if the timezone has not observed daylight savings + * time in the past. If the name of the timezone + * in the given locale is not supported, <code>null</code> + * is returned. + * + * @param id a time zone identifier. + * @param daylight true if the daylight savings time variant + * should be returned. + * @param style either {@link java.util.TimeZone.LONG} or + * {@link java.util.TimeZone.SHORT} + * @param locale the locale to express the timezone in. + * @return the localized time zone name, or <code>null</code> + * if one is not available. + * @throws NullPointerException if the identifer or locale is null. + * @throws IllegalArgumentException if the style is invalid + * or the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.util.TimeZone#getDisplayName(boolean,int,java.util.Locale) + */ + public abstract String getDisplayName(String id, boolean daylight, + int style, Locale locale); + +} diff --git a/libjava/classpath/java/util/spi/package.html b/libjava/classpath/java/util/spi/package.html new file mode 100644 index 00000000000..1abdeb8b4ad --- /dev/null +++ b/libjava/classpath/java/util/spi/package.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.util.spi package. + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.util.spi</title></head> + +<body> + +<p> +A series of service provider interfaces for use by the +classes in <code>java.util</code>. +</p> +<p><span style="font-weight: bold;">Since</span>: 1.6</p> +</body> +</html> diff --git a/libjava/classpath/java/util/zip/DeflaterEngine.java b/libjava/classpath/java/util/zip/DeflaterEngine.java index 51587165e7c..38a82d8b77b 100644 --- a/libjava/classpath/java/util/zip/DeflaterEngine.java +++ b/libjava/classpath/java/util/zip/DeflaterEngine.java @@ -377,7 +377,8 @@ class DeflaterEngine implements DeflaterConstants && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] - && scan < strend); + && scan < strend) + ; if (scan > best_end) { // if (DeflaterConstants.DEBUGGING && ins_h == 0) diff --git a/libjava/classpath/java/util/zip/ZipInputStream.java b/libjava/classpath/java/util/zip/ZipInputStream.java index 4539828c2b0..df44bb3e808 100644 --- a/libjava/classpath/java/util/zip/ZipInputStream.java +++ b/libjava/classpath/java/util/zip/ZipInputStream.java @@ -238,6 +238,7 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants byte[] tmp = new byte[2048]; while (read(tmp) > 0) ; + /* read will close this entry */ return; } |