diff options
author | Mark Wielaard <mark@gcc.gnu.org> | 2006-08-14 23:12:35 +0000 |
---|---|---|
committer | Mark Wielaard <mark@gcc.gnu.org> | 2006-08-14 23:12:35 +0000 |
commit | ac1ed908de999523efc36f38e69bca1aadfe0808 (patch) | |
tree | 97037d2c09c8384d80531f67ec36a01205df6bdb /libjava/classpath/javax | |
parent | abab460491408e05ea93fb85e1975296a87df504 (diff) | |
download | gcc-ac1ed908de999523efc36f38e69bca1aadfe0808.tar.gz |
Imported GNU Classpath 0.92
2006-08-14 Mark Wielaard <mark@klomp.org>
Imported GNU Classpath 0.92
* HACKING: Add more importing hints. Update automake version
requirement.
* configure.ac (gconf-peer): New enable AC argument.
Add --disable-gconf-peer and --enable-default-preferences-peer
to classpath configure when gconf is disabled.
* scripts/makemake.tcl: Set gnu/java/util/prefs/gconf and
gnu/java/awt/dnd/peer/gtk to bc. Classify
gnu/java/security/Configuration.java as generated source file.
* gnu/java/lang/management/VMGarbageCollectorMXBeanImpl.java,
gnu/java/lang/management/VMMemoryPoolMXBeanImpl.java,
gnu/java/lang/management/VMClassLoadingMXBeanImpl.java,
gnu/java/lang/management/VMRuntimeMXBeanImpl.java,
gnu/java/lang/management/VMMemoryManagerMXBeanImpl.java,
gnu/java/lang/management/VMThreadMXBeanImpl.java,
gnu/java/lang/management/VMMemoryMXBeanImpl.java,
gnu/java/lang/management/VMCompilationMXBeanImpl.java: New VM stub
classes.
* java/lang/management/VMManagementFactory.java: Likewise.
* java/net/VMURLConnection.java: Likewise.
* gnu/java/nio/VMChannel.java: Likewise.
* java/lang/Thread.java (getState): Add stub implementation.
* java/lang/Class.java (isEnum): Likewise.
* java/lang/Class.h (isEnum): Likewise.
* gnu/awt/xlib/XToolkit.java (getClasspathTextLayoutPeer): Removed.
* javax/naming/spi/NamingManager.java: New override for StackWalker
functionality.
* configure, sources.am, Makefile.in, gcj/Makefile.in,
include/Makefile.in, testsuite/Makefile.in: Regenerated.
From-SVN: r116139
Diffstat (limited to 'libjava/classpath/javax')
180 files changed, 17572 insertions, 4137 deletions
diff --git a/libjava/classpath/javax/accessibility/AccessibleContext.java b/libjava/classpath/javax/accessibility/AccessibleContext.java index 972f4feae92..ee3a5287b42 100644 --- a/libjava/classpath/javax/accessibility/AccessibleContext.java +++ b/libjava/classpath/javax/accessibility/AccessibleContext.java @@ -496,7 +496,7 @@ public abstract class AccessibleContext } /** - * Get any supported accessible compoent. The default implementation returns + * Get any supported accessible component. The default implementation returns * null. * * @return the supported component, or null diff --git a/libjava/classpath/javax/crypto/Cipher.java b/libjava/classpath/javax/crypto/Cipher.java index 1f68ea60528..1b56a07f4aa 100644 --- a/libjava/classpath/javax/crypto/Cipher.java +++ b/libjava/classpath/javax/crypto/Cipher.java @@ -639,13 +639,7 @@ public class Cipher public final int getOutputSize(int inputLength) throws IllegalStateException { if (cipherSpi == null) - { - return inputLength; - } - if (state != ENCRYPT_MODE && state != DECRYPT_MODE) - { - throw new IllegalStateException("neither encrypting nor decrypting"); - } + return inputLength; return cipherSpi.engineGetOutputSize(inputLength); } diff --git a/libjava/classpath/javax/crypto/spec/PBEKeySpec.java b/libjava/classpath/javax/crypto/spec/PBEKeySpec.java index d17dc41eef7..54b821a79fd 100644 --- a/libjava/classpath/javax/crypto/spec/PBEKeySpec.java +++ b/libjava/classpath/javax/crypto/spec/PBEKeySpec.java @@ -1,5 +1,5 @@ /* PBEKeySpec.java -- Wrapper for password-based keys. - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -76,47 +76,74 @@ public class PBEKeySpec implements KeySpec /** The salt. */ private byte[] salt; + /** The password state */ + private boolean passwordValid = true; + // Constructors. // ------------------------------------------------------------------------ /** * Create a new PBE key spec with just a password. - * + * <p> + * A copy of the password argument is stored instead of the argument itself. + * * @param password The password char array. */ public PBEKeySpec(char[] password) { - this(password, null, 0, 0); + setPassword(password); + + // load the default values for unspecified variables. + salt = null; + iterationCount = 0; + keyLength = 0; } /** * Create a PBE key spec with a password, salt, and iteration count. - * - * @param password The password char array. - * @param salt The salt bytes. + * <p> + * A copy of the password and salt arguments are stored instead of the + * arguments themselves. + * + * @param password The password char array. + * @param salt The salt bytes. * @param iterationCount The iteration count. + * @throws NullPointerException If salt is null + * @throws IllegalArgumentException If salt is an empty array, or + * iterationCount is negative */ public PBEKeySpec(char[] password, byte[] salt, int iterationCount) { - this(password, salt, iterationCount, 0); + setPassword(password); + setSalt(salt); + setIterationCount(iterationCount); + + // load default values into unspecified variables. + keyLength = 0; } /** - * Create a PBE key spec with a password, salt, iteration count, and - * key length. - * - * @param password The password char array. - * @param salt The salt bytes. + * Create a PBE key spec with a password, salt, iteration count, and key + * length. + * <p> + * A copy of the password and salt arguments are stored instead of the + * arguments themselves. + * + * @param password The password char array. + * @param salt The salt bytes. * @param iterationCount The iteration count. - * @param keyLength The generated key length. + * @param keyLength The generated key length. + * @throws NullPointerException If salt is null + * @throws IllegalArgumentException If salt is an empty array, if + * iterationCount or keyLength is negative */ public PBEKeySpec(char[] password, byte[] salt, int iterationCount, int keyLength) { - this.password = password; - this.salt = salt; - this.iterationCount = iterationCount; - this.keyLength = keyLength; + setPassword(password); + setSalt(salt); + setIterationCount(iterationCount); + setKeyLength(keyLength); } // Instance methods. @@ -124,14 +151,19 @@ public class PBEKeySpec implements KeySpec /** * Clear the password array by filling it with null characters. + * <p> + * This clears the stored copy of the password, not the original char array + * used to create the password. */ public final void clearPassword() { - if (password == null) return; + if (password == null) + return; for (int i = 0; i < password.length; i++) - { - password[i] = '\u0000'; - } + password[i] = '\u0000'; + + // since the password is cleared, it is no longer valid + passwordValid = false; } /** @@ -155,22 +187,95 @@ public class PBEKeySpec implements KeySpec } /** - * Get the password character array. - * - * @return The password. + * Get the password character array copy. + * <p> + * This returns a copy of the password, not the password itself. + * + * @return a clone of the password. + * @throws IllegalStateException If {@link #clearPassword()} has already been + * called. */ public final char[] getPassword() { - return password; + if (! passwordValid) + throw new IllegalStateException("clearPassword() has been called, the " + + "password is no longer valid"); + return (char[]) password.clone(); } /** - * Get the salt bytes. - * + * Get the salt bytes array copy. + * <p> + * This returns a copy of the salt, not the salt itself. + * * @return The salt. */ public final byte[] getSalt() { - return salt; + if (salt != null) + return (byte[]) salt.clone(); + return null; + } + + /** + * Set the password char array. + * <p> + * A copy of the password argument is stored instead of the argument itself. + * + * @param password The password to be set + */ + private void setPassword(char[] password) + { + if (password != null) + this.password = (char[]) password.clone(); + else + this.password = new char[0]; + + passwordValid = true; + } + + /** + * Set the salt byte array. + * <p> + * A copy of the salt arguments is stored instead of the argument itself. + * + * @param salt The salt to be set. + * @throws NullPointerException If the salt is null. + * @throws IllegalArgumentException If the salt is an empty array. + */ + private void setSalt(byte[] salt) + { + if (salt.length == 0) + throw new IllegalArgumentException("salt MUST NOT be an empty byte array"); + + this.salt = (byte[]) salt.clone(); + } + + /** + * Set the iterationCount. + * + * @param iterationCount The iteration count to be set. + * @throws IllegalArgumentException If the iterationCount is negative. + */ + private void setIterationCount(int iterationCount) + { + if (iterationCount < 0) + throw new IllegalArgumentException("iterationCount MUST be positive"); + + this.iterationCount = iterationCount; + } + + /** + * Set the keyLength. + * + * @param keyLength The keyLength to be set. + * @throws IllegalArgumentException if the keyLength is negative. + */ + private void setKeyLength(int keyLength) + { + if (keyLength < 0) + throw new IllegalArgumentException("keyLength MUST be positive"); + + this.keyLength = keyLength; } } diff --git a/libjava/classpath/javax/crypto/spec/SecretKeySpec.java b/libjava/classpath/javax/crypto/spec/SecretKeySpec.java index 4caf51a4615..86c4e05d4c9 100644 --- a/libjava/classpath/javax/crypto/spec/SecretKeySpec.java +++ b/libjava/classpath/javax/crypto/spec/SecretKeySpec.java @@ -133,14 +133,22 @@ public class SecretKeySpec implements KeySpec, SecretKey public boolean equals(Object o) { - byte[] okey = ((SecretKeySpec) o).getEncoded(); - if (key.length != okey.length) return false; - for (int i = 0; i < key.length; i++) + if (o instanceof SecretKeySpec) { - if (key[i] != okey[i]) + byte[] okey = ((SecretKeySpec) o).getEncoded(); + if (key.length != okey.length) return false; + for (int i = 0; i < key.length; i++) + { + if (key[i] != okey[i]) + return false; + } + return algorithm.equals(((SecretKeySpec) o).getAlgorithm()); + } + else + { + return false; } - return algorithm.equals(((SecretKeySpec) o).getAlgorithm()); } public int hashCode() diff --git a/libjava/classpath/javax/imageio/spi/IIORegistry.java b/libjava/classpath/javax/imageio/spi/IIORegistry.java index 3e9b2c289cb..e762a96279f 100644 --- a/libjava/classpath/javax/imageio/spi/IIORegistry.java +++ b/libjava/classpath/javax/imageio/spi/IIORegistry.java @@ -46,6 +46,11 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import gnu.javax.imageio.bmp.BMPImageReaderSpi; +import gnu.javax.imageio.bmp.BMPImageWriterSpi; +import gnu.javax.imageio.gif.GIFImageReaderSpi; +import gnu.javax.imageio.png.PNGImageReaderSpi; + public final class IIORegistry extends ServiceRegistry { private static final HashSet defaultCategories = new HashSet(); @@ -81,6 +86,10 @@ public final class IIORegistry extends ServiceRegistry super(defaultCategories.iterator()); // XXX: Register built-in Spis here. + registerServiceProvider(new PNGImageReaderSpi()); // Register PNG decoder. + registerServiceProvider(new GIFImageReaderSpi()); // Register GIF decoder. + registerServiceProvider(new BMPImageReaderSpi()); + registerServiceProvider(new BMPImageWriterSpi()); Toolkit toolkit = Toolkit.getDefaultToolkit(); if (toolkit instanceof ClasspathToolkit) diff --git a/libjava/classpath/javax/imageio/stream/ImageInputStreamImpl.java b/libjava/classpath/javax/imageio/stream/ImageInputStreamImpl.java index c1a5dd404bc..71eec41a998 100644 --- a/libjava/classpath/javax/imageio/stream/ImageInputStreamImpl.java +++ b/libjava/classpath/javax/imageio/stream/ImageInputStreamImpl.java @@ -81,7 +81,8 @@ public abstract class ImageInputStreamImpl implements ImageInputStream protected void finalize() throws Throwable { - close(); + if (!closed) + close(); } public void flush() @@ -154,38 +155,43 @@ public abstract class ImageInputStreamImpl implements ImageInputStream } catch (IOException e) { - // Ignored. + throw new RuntimeException(e); } } public abstract int read() throws IOException; + public abstract int read(byte[] data, int offset, int len) + throws IOException; + public int read(byte[] data) throws IOException { return read(data, 0, data.length); } - public abstract int read(byte[] data, int offset, int len) - throws IOException; - public int readBit() throws IOException { checkClosed(); - // Calc new bit offset here, readByte resets it. + // Calculate new bit offset here as readByte clears it. int newOffset = (bitOffset + 1) & 0x7; + // Clears bitOffset. byte data = readByte(); - - if (bitOffset != 0) + + // If newOffset is 0 it means we just read the 8th bit in a byte + // and therefore we want to advance to the next byte. Otherwise + // we want to roll back the stream one byte so that future readBit + // calls read bits from the same current byte. + if (newOffset != 0) { - seek(getStreamPosition() - 1); - data = (byte) (data >> (8 - newOffset)); + seek(getStreamPosition() - 1); + data = (byte) (data >> (8 - newOffset)); } - + bitOffset = newOffset; return data & 0x1; } @@ -198,17 +204,13 @@ public abstract class ImageInputStreamImpl implements ImageInputStream if (numBits < 0 || numBits > 64) throw new IllegalArgumentException(); - if (numBits == 0) - return 0L; - long bits = 0L; - + for (int i = 0; i < numBits; i++) { bits <<= 1; bits |= readBit(); } - return bits; } @@ -216,12 +218,15 @@ public abstract class ImageInputStreamImpl implements ImageInputStream throws IOException { byte data = readByte(); + return data != 0; } public byte readByte() throws IOException { + checkClosed(); + int data = read(); if (data == -1) @@ -233,10 +238,7 @@ public abstract class ImageInputStreamImpl implements ImageInputStream public void readBytes(IIOByteBuffer buffer, int len) throws IOException { - int result = read(buffer.getData(), buffer.getOffset(), len); - - if (result == -1 || result < len) - throw new EOFException(); + readFullyPrivate(buffer.getData(), buffer.getOffset(), len); buffer.setLength(len); } @@ -250,13 +252,13 @@ public abstract class ImageInputStreamImpl implements ImageInputStream public double readDouble() throws IOException { - return (double) readLong(); + return Double.longBitsToDouble(readLong()); } public float readFloat() throws IOException { - return (float) readInt(); + return Float.intBitsToFloat(readInt()); } public void readFully(byte[] data) @@ -268,8 +270,7 @@ public abstract class ImageInputStreamImpl implements ImageInputStream public void readFully(byte[] data, int offset, int len) throws IOException { - for (int i = 0; i < len; ++i) - data[offset + i] = readByte(); + readFullyPrivate(data, offset, len); } public void readFully(char[] data, int offset, int len) @@ -317,23 +318,20 @@ public abstract class ImageInputStreamImpl implements ImageInputStream public int readInt() throws IOException { - int result = read(buffer, 0, 4); + readFullyPrivate(buffer, 0, 4); - if (result == -1) - throw new EOFException(); - if (getByteOrder() == ByteOrder.LITTLE_ENDIAN) - { - return ((buffer[0] & 0xff) - + (buffer[1] << 8) - + (buffer[2] << 16) - + (buffer[3] << 24)); - } + return (int) + (((int) (buffer[0] & 0xff) << 0) + | ((int) (buffer[1] & 0xff) << 8) + | ((int) (buffer[2] & 0xff) << 16) + | ((int) (buffer[3] & 0xff) << 24)); - return ((buffer[4] << 24) - + (buffer[3] << 16) - + (buffer[2] << 8) - + (buffer[1] & 0xff)); + return (int) + (((int) (buffer[0] & 0xff) << 24) + + ((int) (buffer[1] & 0xff) << 16) + + ((int) (buffer[2] & 0xff) << 8) + + ((int) (buffer[3] & 0xff) << 0)); } public String readLine() @@ -345,94 +343,101 @@ public abstract class ImageInputStreamImpl implements ImageInputStream boolean eol = false; StringBuffer buffer = new StringBuffer(); - while (!eol && (c = read()) != -1) + c = read(); + if (c == -1) + return null; + + while (!eol) { switch(c) { case '\r': - // Consume following \n' - long oldPosition = getStreamPosition(); - if (read() != '\n') - seek(oldPosition); + // Check for following '\n'. + long oldPosition = getStreamPosition(); + c = read(); + if (c == -1 || c == '\n') + eol = true; + else + { + seek(oldPosition); + eol = true; + } + continue; + case '\n': eol = true; - break; + continue; + default: buffer.append((char) c); break; } + c = read(); + if (c == -1) + eol = true; } - if (c == -1 && buffer.length() == 0) - return null; - return buffer.toString(); } public long readLong() throws IOException { - int result = read(buffer, 0, 8); + readFullyPrivate(buffer, 0, 8); - if (result == -1) - throw new EOFException(); - if (getByteOrder() == ByteOrder.LITTLE_ENDIAN) - { - return ((buffer[0] & 0xff) - + (((buffer[1] & 0xff)) << 8) - + (((buffer[2] & 0xff)) << 16) - + (((buffer[3] & 0xffL)) << 24) - + (((buffer[4] & 0xffL)) << 32) - + (((buffer[5] & 0xffL)) << 40) - + (((buffer[6] & 0xffL)) << 48) - + (((long) buffer[7]) << 56)); - } - - return ((((long) buffer[7]) << 56) - + ((buffer[6] & 0xffL) << 48) - + ((buffer[5] & 0xffL) << 40) - + ((buffer[4] & 0xffL) << 32) - + ((buffer[3] & 0xffL) << 24) - + ((buffer[2] & 0xff) << 16) - + ((buffer[1] & 0xff) << 8) - + (buffer[0] & 0xff)); + return (long) + (((long) (buffer[0] & 0xff) << 0) + | ((long) (buffer[1] & 0xff) << 8) + | ((long) (buffer[2] & 0xff) << 16) + | ((long) (buffer[3] & 0xff) << 24) + | ((long) (buffer[4] & 0xff) << 32) + | ((long) (buffer[5] & 0xff) << 40) + | ((long) (buffer[6] & 0xff) << 48) + | ((long) (buffer[7] & 0xff) << 56)); + + return (long) + (((long) (buffer[0] & 0xff) << 56) + | ((long) (buffer[1] & 0xff) << 48) + | ((long) (buffer[2] & 0xff) << 40) + | ((long) (buffer[3] & 0xff) << 32) + | ((long) (buffer[4] & 0xff) << 24) + | ((long) (buffer[5] & 0xff) << 16) + | ((long) (buffer[6] & 0xff) << 8) + | ((long) (buffer[7] & 0xff) << 0)); } public short readShort() throws IOException { - int result = read(buffer, 0, 2); + readFullyPrivate(buffer, 0, 2); - if (result == -1) - throw new EOFException(); - if (getByteOrder() == ByteOrder.LITTLE_ENDIAN) - { - return (short) ((buffer[0] & 0xff) - + (buffer[1] << 8)); - } + return (short) + (((short) (buffer[0] & 0xff) << 0) + | ((short) (buffer[1] & 0xff) << 8)); - return (short) ((buffer[0] << 8) - + (buffer[1] & 0xff)); + return (short) + (((short) (buffer[0] & 0xff) << 8) + | ((short) (buffer[1] & 0xff) << 0)); } public int readUnsignedByte() throws IOException { - return readByte() & 0xff; + return (int) readByte() & 0xff; } public long readUnsignedInt() throws IOException { - return readInt() & 0xffffffff; + return (long) readInt() & 0xffffffffL; } public int readUnsignedShort() throws IOException { - return readShort() & 0xffff; + return (int) readShort() & 0xffff; } public String readUTF() @@ -442,7 +447,8 @@ public abstract class ImageInputStreamImpl implements ImageInputStream String data; ByteOrder old = getByteOrder(); - setByteOrder(ByteOrder.BIG_ENDIAN); // Strings are always big endian. + // Strings are always big endian. + setByteOrder(ByteOrder.BIG_ENDIAN); try { @@ -483,7 +489,7 @@ public abstract class ImageInputStreamImpl implements ImageInputStream checkClosed(); if (bitOffset < 0 || bitOffset > 7) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("bitOffset not between 0 and 7 inclusive"); this.bitOffset = bitOffset; } @@ -512,4 +518,23 @@ public abstract class ImageInputStreamImpl implements ImageInputStream bitOffset = 0; return num; } + + private void readFullyPrivate (byte[] buf, int offset, int len) throws IOException + { + checkClosed(); + + if (len < 0) + throw new IndexOutOfBoundsException("Negative length: " + len); + + while (len > 0) + { + // read will block until some data is available. + int numread = read (buf, offset, len); + if (numread < 0) + throw new EOFException (); + len -= numread; + offset += numread; + } + bitOffset = 0; + } } diff --git a/libjava/classpath/javax/imageio/stream/MemoryCacheImageInputStream.java b/libjava/classpath/javax/imageio/stream/MemoryCacheImageInputStream.java index 935408c9a00..da8a958bb64 100644 --- a/libjava/classpath/javax/imageio/stream/MemoryCacheImageInputStream.java +++ b/libjava/classpath/javax/imageio/stream/MemoryCacheImageInputStream.java @@ -38,10 +38,11 @@ exception statement from your version. */ package javax.imageio.stream; -import gnu.classpath.NotImplementedException; - +import java.io.BufferedInputStream; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; /** * @author Michael Koch (konqueror@gmx.de) @@ -49,10 +50,15 @@ import java.io.InputStream; public class MemoryCacheImageInputStream extends ImageInputStreamImpl { private InputStream stream; + private BufferedInputStream buffer; + + private int READLIMIT = 2048; public MemoryCacheImageInputStream(InputStream stream) { this.stream = stream; + buffer = new BufferedInputStream(stream); + buffer.mark(READLIMIT); } public void close() @@ -63,10 +69,13 @@ public class MemoryCacheImageInputStream extends ImageInputStreamImpl } public void flushBefore(long position) - throws IOException, NotImplementedException + throws IOException { - // FIXME: Implement me. - throw new Error("not implemented"); + long prevFlushedPosition = getFlushedPosition(); + super.flushBefore(position); + buffer.reset(); + buffer.skip(getFlushedPosition() - prevFlushedPosition); + buffer.mark(READLIMIT); } public boolean isCached() @@ -88,13 +97,33 @@ public class MemoryCacheImageInputStream extends ImageInputStreamImpl throws IOException { setBitOffset(0); - return stream.read(); + int retval = buffer.read(); + + if (retval != -1) + streamPos++; + + return retval; } public int read(byte[] data, int offset, int len) throws IOException { setBitOffset(0); - return stream.read(data, offset, len); + int retval = buffer.read(data, offset, len); + + if (retval != -1) + { + streamPos += retval; + } + + return retval; + } + + public void seek(long position) + throws IOException + { + super.seek(position); + buffer.reset(); + buffer.skip(position - getFlushedPosition()); } } diff --git a/libjava/classpath/javax/management/Attribute.java b/libjava/classpath/javax/management/Attribute.java index 5c4a65e4088..ece949532d0 100644 --- a/libjava/classpath/javax/management/Attribute.java +++ b/libjava/classpath/javax/management/Attribute.java @@ -51,6 +51,11 @@ public class Attribute implements Serializable { /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 2484220110589082382L; + + /** * The attribute name. */ final String m_name; diff --git a/libjava/classpath/javax/management/AttributeList.java b/libjava/classpath/javax/management/AttributeList.java new file mode 100644 index 00000000000..6823df3f28a --- /dev/null +++ b/libjava/classpath/javax/management/AttributeList.java @@ -0,0 +1,220 @@ +/* AttributeList.java -- A list of MBean attributes. + 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 javax.management; + +import java.util.ArrayList; + +/** + * Represents a list of MBean {@link Attribute}s, with their + * names and values. This is implemented as an + * {@link java.util.ArrayList} extension, with additional + * methods typed to only allow the addition of {@link Attribute}s. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class AttributeList + extends ArrayList +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = -4077085769279709076L; + + /** + * Constructs an empty list with an initial capacity of ten. + * + * @see java.util.ArrayList#ArrayList() + */ + public AttributeList() + { + super(); + } + + /** + * Constructs an {@link AttributeList} using the contents + * of an existing list. The initial capacity is 110% of the + * size of the specified list. + * + * @param list the list to use to fill this list. + * @see java.util.ArrayList#ArrayList(java.util.Collection) + */ + public AttributeList(AttributeList list) + { + super(list); + } + + /** + * Constructs an empty list with the specified initial capacity. + * + * @param capacity the initial capacity of the list. + * @see java.util.ArrayList#ArrayList(int) + */ + public AttributeList(int capacity) + { + super(capacity); + } + + /** + * Adds the specified {@link Attribute} to the end of the list. + * + * @param attribute the attribute to add. + * @see java.util.Arraylist#add(Object) + */ + public void add(Attribute attribute) + { + super.add(attribute); + } + + /** + * <p> + * Adds the specified {@link Attribute} at the supplied index. + * Any attribute already at that index is moved up one place + * in the list to the position <code>(index + 1)</code>. + * Likewise, the attribute at <code>(index + 1)</code> is + * also moved up one place, continuing until the final + * attribute in the list moves to a new position, increasing + * the size of the list. + * </p> + * <p> + * If the index is invalid (i.e. it is smaller than zero, or + * greater than the current size of the list), a + * @link{RuntimeOperationsException} is thrown, which wraps + * the @link{IndexOutOfBoundsException} from the underlying + * array list. + * </p> + * + * @param index the index at which to place the new attribute. + * @param attribute the new attribute to add. + * @throws RuntimeOperationsException if <code>index < 0</code> + * or <code>index > size()</code> + * @see java.util.ArrayList#add(int, Object) + */ + public void add(int index, Attribute attribute) + { + try + { + super.add(index, attribute); + } + catch (IndexOutOfBoundsException e) + { + throw new RuntimeOperationsException(e, "Invalid index."); + } + } + + /** + * Adds all the {@link Attribute}s from the supplied list + * to the end of this list, in the order they are returned + * by the list's {@link java.util.Iterator}. + * + * @param list the list of attributes to add. + * @return true if the list changed. + * @see java.util.ArrayList#addAll(Collection) + */ + public boolean addAll(AttributeList list) + { + return super.addAll(list); + } + + /** + * <p> + * Adds all the {@link Attribute}s from the supplied list + * to this list, at the specified index. The attributes + * are added in the order they are returned by the + * list's {@link java.util.Iterator}. Any attribute already + * at that index is moved up one place in the list to the + * position <code>(index + list.size())</code>. + * Likewise, the attribute at <code>(index + list.size())</code> + * is also moved up one place, continuing until the final + * attribute in the original list. + * </p> + * <p> + * If the index is invalid (i.e. it is smaller than zero, or + * greater than the current size of the list), a + * @link{RuntimeOperationsException} is thrown, which wraps + * the @link{IndexOutOfBoundsException} from the underlying + * array list. + * </p> + * + * @param index the index at which to place the new attribute. + * @param list the list of attributes to add. + * @return true if the list changed. + * @throws RuntimeOperationsException if <code>index < 0</code> + * or <code>index > size()</code> + * @see java.util.ArrayList#addAll(int, Collection) + */ + public boolean addAll(int index, AttributeList list) + { + try + { + return super.addAll(index, list); + } + catch (IndexOutOfBoundsException e) + { + throw new RuntimeOperationsException(e, "Invalid index."); + } + } + + /** + * Replaces the attribute at the specified index with the one + * supplied. If the index is invalid (i.e. it is smaller than + * zero, or greater than the current size of the list), a + * @link{RuntimeOperationsException} is thrown, which wraps + * the @link{IndexOutOfBoundsException} from the underlying + * array list. + * + * @param index the index at which to place the new attribute. + * @param attribute the new attribute to add. + * @throws RuntimeOperationsException if <code>index < 0</code> + * or <code>index > size()</code> + * @see java.util.ArrayList#set(int, Object) + */ + public void set(int index, Attribute attribute) + { + try + { + super.set(index, attribute); + } + catch (IndexOutOfBoundsException e) + { + throw new RuntimeOperationsException(e, "Invalid index."); + } + } + +} diff --git a/libjava/classpath/javax/management/AttributeNotFoundException.java b/libjava/classpath/javax/management/AttributeNotFoundException.java new file mode 100644 index 00000000000..2c6c698ebae --- /dev/null +++ b/libjava/classpath/javax/management/AttributeNotFoundException.java @@ -0,0 +1,71 @@ +/* AttributeNotFoundException.java -- Thrown by unknown attributes. + 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 javax.management; + +/** + * Thrown when a attribute is requested but can not be + * found. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class AttributeNotFoundException + extends OperationsException +{ + + /** + * Constructs a new <code>AttributeNotFoundException</code>. + */ + public AttributeNotFoundException() + { + super(); + } + + /** + * Constructs a new <code>AttributeNotFoundException</code> + * with the specified message. + * + * @param message the error message to give to the user. + */ + public AttributeNotFoundException(String message) + { + super(message); + } + +} + diff --git a/libjava/classpath/javax/management/DynamicMBean.java b/libjava/classpath/javax/management/DynamicMBean.java new file mode 100644 index 00000000000..80ea72cf43c --- /dev/null +++ b/libjava/classpath/javax/management/DynamicMBean.java @@ -0,0 +1,162 @@ +/* DynamicMBean.java -- A management bean with a dynamic interface. + 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 javax.management; + +/** + * Represents a management bean that provides a + * dynamic interface. Users of a {@link DynamicMBean} + * may retrieve information about its attributes at + * runtime and use this information to dynamically + * obtain the corresponding values of these attributes. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface DynamicMBean +{ + + /** + * Obtains the value of the specified attribute of the + * management bean. The management bean should perform + * a lookup for the named attribute, and return its value + * by calling the appropriate getter method, if possible. + * + * @param name the name of the attribute to retrieve. + * @return the value of the specified attribute. + * @throws AttributeNotFoundException if the name does not + * correspond to an attribute + * of the bean. + * @throws MBeanException if retrieving the attribute causes + * the bean to throw an exception (which + * becomes the cause of this exception). + * @throws ReflectionException if an exception occurred in trying + * to use the reflection interface + * to lookup the attribute. The + * thrown exception is the cause of + * this exception. + * @see #setAttribute(String) + */ + Object getAttribute(String name) + throws AttributeNotFoundException, MBeanException, + ReflectionException; + + /** + * Obtains the values of each of the specified attributes + * of the management bean. The returned list includes + * those attributes that were retrieved and their + * corresponding values. + * + * @param names the names of the attributes to retrieve. + * @return a list of the retrieved attributes. + * @see #setAttributes(AttributeList) + */ + AttributeList getAttributes(String[] names); + + /** + * Returns an information object which lists the attributes + * and actions associated with the management bean. + * + * @return a description of the management bean, including + * all exposed attributes and actions. + */ + MBeanInfo getMBeanInfo(); + + /** + * Invokes the specified action on the management bean using + * the supplied parameters. The signature of the action is + * specified by a {@link String} array, which lists the classes + * corresponding to each parameter. The class loader used to + * load these classes is the same as that used for loading the + * management bean itself. + * + * @param name the name of the action to invoke. + * @param params the parameters used to call the action. + * @param signature the signature of the action. + * @return the return value of the action. + * @throws MBeanException if the action throws an exception. The + * thrown exception is the cause of this + * exception. + * @throws ReflectionException if an exception occurred in trying + * to use the reflection interface + * to invoke the action. The + * thrown exception is the cause of + * this exception. + */ + Object invoke(String name, Object[] params, String[] signature) + throws MBeanException, ReflectionException; + + /** + * Sets the value of the specified attribute of the + * management bean. The management bean should perform + * a lookup for the named attribute, and sets its value + * using the associated setter method, if possible. + * + * @param attribute the attribute to set. + * @throws AttributeNotFoundException if the attribute does not + * correspond to an attribute + * of the bean. + * @throws InvalidAttributeValueException if the value is invalid + * for this particular + * attribute of the bean. + * @throws MBeanException if setting the attribute causes + * the bean to throw an exception (which + * becomes the cause of this exception). + * @throws ReflectionException if an exception occurred in trying + * to use the reflection interface + * to lookup the attribute. The + * thrown exception is the cause of + * this exception. + * @see #getAttribute(String) + */ + void setAttribute(Attribute attribute) + throws AttributeNotFoundException, InvalidAttributeValueException, + MBeanException, ReflectionException; + + /** + * Sets the value of each of the specified attributes + * to that supplied by the {@link Attribute} object. + * The returned list contains the attributes that were + * set and their new values. + * + * @param attributes the attributes to set. + * @return a list of the changed attributes. + * @see #getAttributes(AttributeList) + */ + AttributeList setAttributes(AttributeList attributes); + +} diff --git a/libjava/classpath/javax/management/IntrospectionException.java b/libjava/classpath/javax/management/IntrospectionException.java new file mode 100644 index 00000000000..3b9d4b1ef6c --- /dev/null +++ b/libjava/classpath/javax/management/IntrospectionException.java @@ -0,0 +1,78 @@ +/* IntrospectionException.java -- Thrown by bean introspection methods. + 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 javax.management; + +/** + * A general for all exceptions thrown during introspection + * operations on management beans. For example, such an + * exception may be thrown while constructing one of + * the <code>MBeanXXXInfo</code> classes. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IntrospectionException + extends OperationsException +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 1054516935875481725L; + + /** + * Constructs a new <code>IntrospectionException</code>. + */ + public IntrospectionException() + { + super(); + } + + /** + * Constructs a new <code>IntrospectionException</code> + * with the specified message. + * + * @param message the error message to give to the user. + */ + public IntrospectionException(String message) + { + super(message); + } + +} + diff --git a/libjava/classpath/javax/management/InvalidAttributeValueException.java b/libjava/classpath/javax/management/InvalidAttributeValueException.java new file mode 100644 index 00000000000..f766eda7f3a --- /dev/null +++ b/libjava/classpath/javax/management/InvalidAttributeValueException.java @@ -0,0 +1,71 @@ +/* InvalidAttributeValueException.java -- Thrown by invalid values. + 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 javax.management; + +/** + * Thrown when a value is given for an attribute which + * is invalid. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class InvalidAttributeValueException + extends OperationsException +{ + + /** + * Constructs a new <code>InvalidAttributeValueException</code>. + */ + public InvalidAttributeValueException() + { + super(); + } + + /** + * Constructs a new <code>InvalidAttributeValueException</code> + * with the specified message. + * + * @param message the error message to give to the user. + */ + public InvalidAttributeValueException(String message) + { + super(message); + } + +} + diff --git a/libjava/classpath/javax/management/JMException.java b/libjava/classpath/javax/management/JMException.java new file mode 100644 index 00000000000..d05702575af --- /dev/null +++ b/libjava/classpath/javax/management/JMException.java @@ -0,0 +1,71 @@ +/* JMException.java -- Thrown by the management classes. + 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 javax.management; + +/** + * A general superclass for all non-runtime management + * exceptions. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class JMException + extends Exception +{ + + /** + * Constructs a new <code>JMException</code>. + */ + public JMException() + { + super(); + } + + /** + * Constructs a new <code>JMException</code> + * with the specified message. + * + * @param message the error message to give to the user. + */ + public JMException(String message) + { + super(message); + } + +} + diff --git a/libjava/classpath/javax/management/JMRuntimeException.java b/libjava/classpath/javax/management/JMRuntimeException.java new file mode 100644 index 00000000000..ba14a19c4ce --- /dev/null +++ b/libjava/classpath/javax/management/JMRuntimeException.java @@ -0,0 +1,71 @@ +/* JMRuntimeException.java -- Thrown by the management classes. + 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 javax.management; + +/** + * A general superclass for all runtime management + * exceptions. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class JMRuntimeException + extends RuntimeException +{ + + /** + * Constructs a new <code>JMRuntimeException</code>. + */ + public JMRuntimeException() + { + super(); + } + + /** + * Constructs a new <code>JMRuntimeException</code> + * with the specified message. + * + * @param message the error message to give to the user. + */ + public JMRuntimeException(String message) + { + super(message); + } + +} + diff --git a/libjava/classpath/javax/management/ListenerNotFoundException.java b/libjava/classpath/javax/management/ListenerNotFoundException.java new file mode 100644 index 00000000000..ea3cb74161b --- /dev/null +++ b/libjava/classpath/javax/management/ListenerNotFoundException.java @@ -0,0 +1,75 @@ +/* ListenerNotFoundException.java -- Thrown when a listener does not exist. + 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 javax.management; + +/** + * Thrown when a requested listener does not exist. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class ListenerNotFoundException + extends OperationsException +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = -7242605822448519061L; + + /** + * Constructs a new <code>ListenerNotFoundException</code>. + */ + public ListenerNotFoundException() + { + super(); + } + + /** + * Constructs a new <code>ListenerNotFoundException</code> + * with the specified message. + * + * @param message the error message to give to the user. + */ + public ListenerNotFoundException(String message) + { + super(message); + } + +} + diff --git a/libjava/classpath/javax/management/MBeanAttributeInfo.java b/libjava/classpath/javax/management/MBeanAttributeInfo.java new file mode 100644 index 00000000000..bded7e41a07 --- /dev/null +++ b/libjava/classpath/javax/management/MBeanAttributeInfo.java @@ -0,0 +1,301 @@ +/* MBeanAttributeInfo.java -- Information about an attribute of a bean. + 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 javax.management; + +import java.lang.reflect.Method; + +/** + * Describes the attributes of a management bean. + * The information in this class is immutable as standard. + * Of course, subclasses may change this, but this + * behaviour is not recommended. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MBeanAttributeInfo + extends MBeanFeatureInfo + implements Cloneable +{ + + /** + * The type of the attribute. + * + * @serial the attribute type. + */ + private String attributeType; + + /** + * True if the attribute's value can be changed. + * + * @serial true if the value can be changed. + */ + private boolean isWrite; + + /** + * True if the attribute's value can be read. + * + * @serial true if the value can be read. + */ + private boolean isRead; + + /** + * True if the attribute is a boolean and thus + * has a isXXX accessor rather than a getXXX accessor. + * + * @serial true if the attribute has an isXXX accessor. + */ + private boolean is; + + /** + * Constructs a new {@link MBeanAttributeInfo} using the specified + * name and description, with the given accessor and mutator + * methods. A <code>null</code> value for the accessor method + * indicates that the value can not be read. A <code>null</code> + * value for the mutator method indicates that the value can not be + * changed. + * + * @param name the name of the attribute. + * @param desc a description of the attribute. + * @param getter the accessor method, or <code>null</code> if the value + * can not be read. + * @param setter the mutator method, or <code>null</code> if the value + * can not be changed. + * @throws IntrospectionException if both the accessor and mutator method + * are <code>null</code>. + */ + public MBeanAttributeInfo(String name, String desc, + Method getter, Method setter) + throws IntrospectionException + { + super(name, desc); + if (getter == null && setter == null) + throw new IntrospectionException("Both the getter and setter methods can " + + "not be null."); + if (getter == null) + { + attributeType = setter.getParameterTypes()[0].getName(); + isRead = false; + is = false; + } + else + { + attributeType = getter.getReturnType().getName(); + isRead = true; + is = getter.getName().startsWith("is"); + } + if (setter != null) + isWrite = true; + } + + /** + * Constructs a new {@link MBeanAttributeInfo} using the specified + * name, description and type with the given settings for the accessor + * and mutator methods. + * + * @param name the name of the attribute. + * @param type the type of the attribute, in the form of its class name. + * @param desc a description of the attribute. + * @param isReadable true if the attribute's value can be read. + * @param isWritable true if the attribute's value can be changed. + * @param isIs true if the attribute uses an accessor of the form isXXX. + * @throws IllegalArgumentException if the attribute is both unreadable + * and unwritable. + */ + public MBeanAttributeInfo(String name, String type, String desc, + boolean isReadable, boolean isWritable, + boolean isIs) + { + super(name, desc); + if (!isReadable && !isWritable) + throw new IllegalArgumentException("The attribute can not be both " + + "unreadable and unwritable."); + attributeType = type; + isRead = isReadable; + isWrite = isWritable; + is = isIs; + } + + /** + * Returns a clone of this instance. The clone is created + * using just the method provided by {@link java.lang.Object}. + * Thus, the clone is just a shallow clone as returned by + * that method, and does not contain any deeper cloning based + * on the subject of this class. + * + * @return a clone of this instance. + * @see java.lang.Cloneable + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + /* This shouldn't happen; we implement Cloneable */ + throw new IllegalStateException("clone() called on " + + "non-cloneable object."); + } + } + + /** + * Compares this feature with the supplied object. This + * returns true iff the object is an instance of + * {@link MBeanAttributeInfo}, {@link Object#equals()} + * returns true for a comparison of both the name and + * description of this attribute with that of the specified + * object (performed by the superclass), and the type and + * boolean flags of the two instances are equal. + * + * @param obj the object to compare. + * @return true if the object is a {@link MBeanAttributeInfo} + * instance, + * <code>name.equals(object.getName())</code>, + * <code>description.equals(object.getDescription())</code>, + * <code>attributeType.equals(object.getType())</code>, + * <code>isRead == object.isReadable()</code>, + * <code>isWrite == object.isWritable()</code>, + * <code>is == object.isIs()</code> + */ + public boolean equals(Object obj) + { + if (!(obj instanceof MBeanAttributeInfo)) + return false; + if (!(super.equals(obj))) + return false; + MBeanAttributeInfo o = (MBeanAttributeInfo) obj; + return (attributeType.equals(o.getType()) && + isRead == o.isReadable() && + isWrite == o.isWritable() && + is == o.isIs()); + } + + /** + * Returns the type of this attribute, in the form of its class name. + * + * @return the type of this attribute. + */ + public String getType() + { + return attributeType; + } + + /** + * Returns the hashcode of the attribute information as the sum of + * the hashcode of the superclass, the hashcode of the type, + * the hashcode of {@link #isReadable()}, twice the hashcode + * of {@link #isWritable()} and four times the hashcode + * of {@link #isIs()}. + * + * @return the hashcode of the attribute information. + */ + public int hashCode() + { + return super.hashCode() + attributeType.hashCode() + + Boolean.valueOf(isRead).hashCode() + + (2 * Boolean.valueOf(isWrite).hashCode()) + + (4 * Boolean.valueOf(is).hashCode()); + } + + /** + * Returns true if the accessor method of this attribute + * is of the form <code>isXXX</code>. + * + * @return true if the accessor takes the form <code>isXXX</code>. + */ + public boolean isIs() + { + return is; + } + + /** + * Returns true if value of this attribute can be read. + * + * @return true if the value of the attribute can be read. + */ + public boolean isReadable() + { + return isRead; + } + + /** + * Returns true if the value of this attribute can be changed. + * + * @return true if the value of the attribute can be changed. + */ + public boolean isWritable() + { + return isWrite; + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.MBeanAttributeInfo</code>), + * the name, description and type of the attribute and the + * current settings of the {@link #isReadable()}, + * {@link #isWritable()} and {@link #isIs()} properties. + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + { + super.toString(); + string = string.substring(0, string.length() - 1) + + ",type=" + attributeType + + ",isReadable=" + (isRead ? "yes" : "no") + + ",isWritable=" + (isWrite ? "yes" : "no") + + ",isIs=" + (is ? "yes" : "no") + + "]"; + } + return string; + } + +} diff --git a/libjava/classpath/javax/management/MBeanConstructorInfo.java b/libjava/classpath/javax/management/MBeanConstructorInfo.java new file mode 100644 index 00000000000..832a3296d6b --- /dev/null +++ b/libjava/classpath/javax/management/MBeanConstructorInfo.java @@ -0,0 +1,228 @@ +/* MBeanConstructorInfo.java -- Information about a bean's constructor. + 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 javax.management; + +import java.lang.reflect.Constructor; + +import java.util.Arrays; + +/** + * Describes the constructors of a management bean. + * The information in this class is immutable as standard. + * Of course, subclasses may change this, but this + * behaviour is not recommended. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MBeanConstructorInfo + extends MBeanFeatureInfo + implements Cloneable +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 4433990064191844427L; + + /** + * The signature of the constructor i.e. the argument types. + */ + private MBeanParameterInfo[] signature; + + /** + * Constructs a @link{MBeanConstructorInfo} with the specified + * description using the given constructor. Each parameter is + * described merely by its type; the name and description are + * <code>null</code>. + * + * @param desc a description of the attribute. + * @param cons the constructor. + */ + public MBeanConstructorInfo(String desc, Constructor cons) + { + super(cons.getName(), desc); + Class[] paramTypes = cons.getParameterTypes(); + signature = new MBeanParameterInfo[paramTypes.length]; + for (int a = 0; a < paramTypes.length; ++a) + signature[a] = new MBeanParameterInfo(null, + paramTypes[a].getName(), + null); + } + + /** + * Constructs a @link{MBeanConstructorInfo} with the specified + * name, description and parameter information. A <code>null</code> + * value for the parameter information is the same as passing in + * an empty array. + * + * @param name the name of the constructor. + * @param desc a description of the attribute. + * @param sig the signature of the constructor, as a series + * of {@link MBeanParameterInfo} objects, one for + * each parameter. + */ + public MBeanConstructorInfo(String name, String desc, + MBeanParameterInfo[] sig) + { + super(name, desc); + if (sig == null) + signature = new MBeanParameterInfo[0]; + else + signature = sig; + } + + /** + * Returns a clone of this instance. The clone is created + * using just the method provided by {@link java.lang.Object}. + * Thus, the clone is just a shallow clone as returned by + * that method, and does not contain any deeper cloning based + * on the subject of this class. + * + * @return a clone of this instance. + * @see java.lang.Cloneable + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + /* This shouldn't happen; we implement Cloneable */ + throw new IllegalStateException("clone() called on " + + "non-cloneable object."); + } + } + + /** + * Compares this feature with the supplied object. This returns + * true iff the object is an instance of {@link + * MBeanConstructorInfo}, {@link Object#equals()} returns true for a + * comparison of both the name and description of this notification + * with that of the specified object (performed by the superclass), + * and the two signature arrays contain the same elements in the + * same order (but one may be longer than the other). + * + * @param obj the object to compare. + * @return true if the object is a {@link MBeanConstructorInfo} + * instance, + * <code>name.equals(object.getName())</code>, + * <code>description.equals(object.getDescription())</code> + * and the corresponding elements of the signature arrays are + * equal. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof MBeanConstructorInfo)) + return false; + if (!(super.equals(obj))) + return false; + MBeanConstructorInfo o = (MBeanConstructorInfo) obj; + MBeanParameterInfo[] sig = o.getSignature(); + for (int a = 0; a < signature.length; ++a) + { + if (a == sig.length) + return true; + if (!(signature[a].equals(sig[a]))) + return false; + } + return true; + } + + /** + * Returns the constructor's signature, in the form of + * information on each parameter. Each parameter is + * described by an instance of {@link MBeanParameterInfo}. + * The returned array is a shallow copy of the array used + * by this instance, so changing which elements are stored + * in the array won't affect the array used by this, but + * changing the actual elements will affect the ones used + * here. + * + * @return an array of {@link MBeanParameterInfo} objects, + * describing the constructor parameters. + */ + public MBeanParameterInfo[] getSignature() + { + return (MBeanParameterInfo[]) signature.clone(); + } + + /** + * Returns the hashcode of the constructor information as the sum + * of the hashcode of the superclass and the hashcode of the parameter + * array. + * + * @return the hashcode of the constructor information. + */ + public int hashCode() + { + return super.hashCode() + Arrays.hashCode(signature); + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.MBeanConstructorInfo</code>), + * the name and description of the constructor and the + * contents of the array of parameters. + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + { + super.toString(); + string = string.substring(0, string.length() - 1) + + ",signature=" + Arrays.toString(signature) + + "]"; + } + return string; + } + +} diff --git a/libjava/classpath/javax/management/MBeanException.java b/libjava/classpath/javax/management/MBeanException.java new file mode 100644 index 00000000000..f082275e69b --- /dev/null +++ b/libjava/classpath/javax/management/MBeanException.java @@ -0,0 +1,118 @@ +/* MBeanException.java -- A user-defined management exception. + 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 javax.management; + +/** + * Represents an arbitrary exception thrown by a management + * bean. When a management bean executes code that causes + * an exception to be thrown, the resulting exception is + * wrapped inside an {@link MBeanException}. Calling + * {@link getTargetException()} will return the wrapped + * exception. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MBeanException + extends JMException +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 4066342430588744142L; + + /* Sun re-implemented causality -- don't know why, but + serialization demands we do too... */ + + /** + * The target exception. + * + * @serial the target exception. + */ + private Exception exception; + + /** + * Constructs a new <code>MBeanException</code> wrapping + * the specified exception. + * + * @param e the exception to be wrapped. + */ + public MBeanException(Exception e) + { + super(); + exception = e; + } + + /** + * Constructs a new <code>MBeanException</code> wrapping + * the specified exception and using the supplied message. + * + * @param e the exception to be wrapped. + * @param message the error message to give to the user. + */ + public MBeanException(Exception e, String message) + { + super(message); + exception = e; + } + + /** + * Returns the true cause of this exception, the wrapped + * exception. + * + * @return the wrapped exception. + */ + public Throwable getCause() + { + return exception; + } + + /** + * Returns the true cause of this exception, the wrapped + * exception. + * + * @return the wrapped exception. + */ + public Exception getTargetException() + { + return exception; + } + +} + diff --git a/libjava/classpath/javax/management/MBeanFeatureInfo.java b/libjava/classpath/javax/management/MBeanFeatureInfo.java new file mode 100644 index 00000000000..4f0243e1894 --- /dev/null +++ b/libjava/classpath/javax/management/MBeanFeatureInfo.java @@ -0,0 +1,186 @@ +/* MBeanFeatureInfo.java -- Information about a bean feature. + 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 javax.management; + +import java.io.Serializable; + +/** + * A general superclass for the description of features + * of management beans. This allows the user to access + * the feature dynamically, without knowing the details + * beforehand. The information is immutable as standard. + * Of course, subclasses may change this, but this + * behaviour is not recommended. + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MBeanFeatureInfo + implements Serializable +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 3952882688968447265L; + + /** + * A description of the feature in human-readable form. + * Subclasses should access this via the {@link #getDescription()} + * function rather than using the value directly. + * + * @serial a description of the feature. + */ + protected String description; + + /** + * The name of the feature. Subclasses should access this + * via the {@link #getName()} function rather than using the + * value directly. + * + * @serial the name of the feature. + */ + protected String name; + + /** + * The <code>toString()</code> result of this instance. + */ + protected transient String string; + + /** + * Constructs a new {@link MBeanFeatureInfo} with the specified + * name and description. + * + * @param name the name of the management bean feature. + * @param description the description of the feature. + */ + public MBeanFeatureInfo(String name, String description) + { + this.name = name; + this.description = description; + } + + /** + * Compares this feature with the supplied object. This + * returns true iff the object is an instance of + * {@link MBeanFeatureInfo} and {@link Object#equals()} + * returns true for a comparison of both the name and + * description of this feature with that of the specified + * object. + * + * @param obj the object to compare. + * @return true if the object is a {@link MBeanFeatureInfo} + * instance, + * <code>name.equals(object.getName())</code> and + * <code>description.equals(object.getDescription</code>. + */ + public boolean equals(Object obj) + { + if (obj instanceof MBeanFeatureInfo) + { + MBeanFeatureInfo o = (MBeanFeatureInfo) obj; + return ((name == null ? + o.getName() == null : + name.equals(o.getName())) && + (description == null ? + o.getDescription() == null : + description.equals(o.getDescription()))); + } + else + return false; + } + + /** + * Returns a description of this feature. + * + * @return a human-readable description. + */ + public String getDescription() + { + return description; + } + + /** + * Returns the name of this feature. + * + * @return the name of the feature. + */ + public String getName() + { + return name; + } + + /** + * Returns the hashcode of the feature as + * the sum of the hashcodes of its name + * and description. + * + * @return the hashcode of this feature. + */ + public int hashCode() + { + return (name == null ? -1 : name.hashCode()) + + (description == null ? -1 : description.hashCode()); + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.MBeanFeatureInfo</code>) and + * the name and description of the feature. + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + string = getClass().getName() + + "[name=" + name + + ",desc=" + description + + "]"; + return string; + } + +} diff --git a/libjava/classpath/javax/management/MBeanInfo.java b/libjava/classpath/javax/management/MBeanInfo.java new file mode 100644 index 00000000000..e6f03f0656e --- /dev/null +++ b/libjava/classpath/javax/management/MBeanInfo.java @@ -0,0 +1,397 @@ +/* MBeanInfo.java -- Information about a management bean. + 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 javax.management; + +import java.io.Serializable; + +import java.util.Arrays; + +/** + * <p> + * Describes the interface of a management bean. This allows + * the user to access the bean dynamically, without knowing + * the details of any of its attributes, operations, + * constructors or notifications beforehand. The information + * is immutable as standard. Of course, subclasses may change + * this, but this behaviour is not recommended. + * </p> + * <p> + * The contents of this class, for standard management beans, + * are dynamically compiled using reflection. + * {@link #getClassName()} and {@link #getConstructors()} + * return the name of the class and its constructors, respectively. + * This is much the same as could be obtained by reflection on the + * bean. {@link #getAttributes()} and {@link #getOperations()}, + * however, do something more in splitting the methods of the + * class into two sets. Those of the form, <code>getXXX</code>, + * <code>setXXX</code> and <code>isXXX</code> are taken to be + * the accessors and mutators of a series of attributes, with + * <code>XXX</code> being the attribute name. These are returned + * by {@link getAttributes()} and the {@link Attribute} class can + * be used to manipulate them. The remaining methods are classified + * as operations and returned by {@link getOperations()}. + * </p> + * <p> + * Beans can also broadcast notifications. If the bean provides this + * facility, by implementing the {@link NotificationBroadcaster} + * interface, then an array of {@link MBeanNotificationInfo} objects + * may be obtained from {@link #getNotifications()}, which describe + * the notifications emitted. + * </p> + * <p> + * Model management beans and open management beans also supply an + * instance of this class, as part of implementing the + * {@link DynamicMBean#getMBeanInfo()} method of {@link DynamicMBean}. + * </p> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MBeanInfo + implements Cloneable, Serializable +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = -6451021435135161911L; + + /** + * A description of the bean. + * + * @serial The bean's description. + */ + private String description; + + /** + * The class name of the management bean. + * + * @serial The bean's class name. + */ + private String className; + + /** + * Descriptions of the attributes provided by the bean. + */ + private MBeanAttributeInfo[] attributes; + + /** + * Descriptions of the operations provided by the bean. + */ + private MBeanOperationInfo[] operations; + + /** + * Descriptions of the bean's constructors. + */ + private MBeanConstructorInfo[] constructors; + + /** + * Descriptions of the notifications emitted by the bean. + * + * @serial The bean's notifications. + */ + private MBeanNotificationInfo[] notifications; + + /** + * The <code>toString()</code> result of this instance. + */ + private transient String string; + + /** + * Constructs a new {@link MBeanInfo} using the supplied + * class name and description with the given attributes, + * operations, constructors and notifications. The class + * name does not have to actually specify a valid class that + * can be loaded by the MBean server or class loader; it merely + * has to be a syntactically correct class name. Any of the + * arrays may be <code>null</code>; this will be treated as if + * an empty array was supplied. + * + * @param name the name of the class this instance describes. + * @param desc a description of the bean. + * @param attribs the attribute descriptions for the bean, + * or <code>null</code>. + * @param cons the constructor descriptions for the bean, + * or <code>null</code>. + * @param ops the operation descriptions for the bean, + * or <code>null</code>. + * @param notifs the notification descriptions for the bean, + * or <code>null</code>. + */ + public MBeanInfo(String name, String desc, MBeanAttributeInfo[] attribs, + MBeanConstructorInfo[] cons, MBeanOperationInfo[] ops, + MBeanNotificationInfo[] notifs) + { + className = name; + description = desc; + if (attribs == null) + attributes = new MBeanAttributeInfo[0]; + else + attributes = attribs; + if (cons == null) + constructors = new MBeanConstructorInfo[0]; + else + constructors = cons; + if (ops == null) + operations = new MBeanOperationInfo[0]; + else + operations = ops; + if (notifs == null) + notifications = new MBeanNotificationInfo[0]; + else + notifications = notifs; + } + + /** + * Returns a shallow clone of the information. This is + * simply a new copy of each string and a clone + * of each array, which still references the same objects, + * as obtained by the {@link Object} implementation of + * {@link Object#clone()}. As the fields can not be + * changed, this method is only really of interest to + * subclasses which may add new mutable fields or make + * the existing ones mutable. + * + * @return a shallow clone of this {@link MBeanInfo}. + */ + public Object clone() + { + MBeanInfo clone = null; + try + { + clone = (MBeanInfo) super.clone(); + } + catch (CloneNotSupportedException e) + { + /* This won't happen as we implement Cloneable */ + } + return clone; + } + + /** + * Compares this feature with the supplied object. This returns + * true iff the object is an instance of {@link MBeanInfo} and + * {@link Object#equals()} returns true for a comparison of the + * class name and description, and the arrays each contain the same + * elements in the same order (but one may be longer than the + * other). + * + * @param obj the object to compare. + * @return true if the object is a {@link MBeanInfo} + * instance, + * <code>className.equals(object.getClassName())</code>, + * <code>description.equals(object.getDescription())</code> + * and the corresponding elements of the arrays are + * equal. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof MBeanInfo)) + return false; + if (!(super.equals(obj))) + return false; + MBeanInfo o = (MBeanInfo) obj; + MBeanAttributeInfo[] attr = o.getAttributes(); + for (int a = 0; a < attributes.length; ++a) + { + if (a == attr.length) + return true; + if (!(attributes[a].equals(attr[a]))) + return false; + } + MBeanConstructorInfo[] cons = o.getConstructors(); + for (int a = 0; a < constructors.length; ++a) + { + if (a == cons.length) + return true; + if (!(constructors[a].equals(cons[a]))) + return false; + } + MBeanOperationInfo[] ops = o.getOperations(); + for (int a = 0; a < operations.length; ++a) + { + if (a == ops.length) + return true; + if (!(operations[a].equals(ops[a]))) + return false; + } + MBeanNotificationInfo[] notifs = o.getNotifications(); + for (int a = 0; a < notifications.length; ++a) + { + if (a == notifs.length) + return true; + if (!(notifications[a].equals(notifs[a]))) + return false; + } + return (className.equals(o.getClassName()) && + description.equals(o.getDescription())); + } + + /** + * Returns descriptions of each of the attributes provided + * by this management bean. The returned value is a shallow + * copy of the attribute array maintained by this instance. + * Hence, changing the elements of the returned array will not + * affect the attribute array, and the elements (instances + * of the {@link MBeanAttributeInfo} class) are immutable. + * + * @return an array of {@link MBeanAttributeInfo} objects, + * representing the attributes emitted by this + * management bean. + */ + public MBeanAttributeInfo[] getAttributes() + { + return (MBeanAttributeInfo[]) attributes.clone(); + } + + /** + * Returns the class name of the management bean. + * + * @return the bean's class name. + */ + public String getClassName() + { + return className; + } + + /** + * Returns descriptions of each of the constructors provided + * by this management bean. The returned value is a shallow + * copy of the constructor array maintained by this instance. + * Hence, changing the elements of the returned array will not + * affect the constructor array, and the elements (instances + * of the {@link MBeanConstructorInfo} class) are immutable. + * + * @return an array of {@link MBeanConstructorInfo} objects, + * representing the constructors emitted by this + * management bean. + */ + public MBeanConstructorInfo[] getConstructors() + { + return (MBeanConstructorInfo[]) constructors.clone(); + } + + /** + * Returns a description of the management bean. + * + * @return the bean's description. + */ + public String getDescription() + { + return description; + } + + /** + * Returns descriptions of each of the notifications emitted + * by this management bean. The returned value is a shallow + * copy of the notification array maintained by this instance. + * Hence, changing the elements of the returned array will not + * affect the notification array, and the elements (instances + * of the {@link MBeanNotificationInfo} class) are immutable. + * + * @return an array of {@link MBeanNotificationInfo} objects, + * representing the notifications emitted by this + * management bean. + */ + public MBeanNotificationInfo[] getNotifications() + { + return (MBeanNotificationInfo[]) notifications.clone(); + } + + /** + * Returns descriptions of each of the operations provided + * by this management bean. The returned value is a shallow + * copy of the operation array maintained by this instance. + * Hence, changing the elements of the returned array will not + * affect the operation array, and the elements (instances + * of the {@link MBeanOperationInfo} class) are immutable. + * + * @return an array of {@link MBeanOperationInfo} objects, + * representing the operations emitted by this + * management bean. + */ + public MBeanOperationInfo[] getOperations() + { + return (MBeanOperationInfo[]) operations.clone(); + } + + /** + * Returns the hashcode of the information as the sum of the + * hashcode of the classname, description and each array. + * + * @return the hashcode of the information. + */ + public int hashCode() + { + return className.hashCode() + description.hashCode() + + Arrays.hashCode(attributes) + Arrays.hashCode(constructors) + + Arrays.hashCode(operations) + Arrays.hashCode(notifications); + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.MBeanInfo</code>), + * the name and description of the bean and the contents + * of the four arrays. + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + string = getClass().getName() + + "[name=" + className + + ",desc=" + description + + ",attributes=" + Arrays.toString(attributes) + + ",constructors=" + Arrays.toString(constructors) + + ",operations=" + Arrays.toString(operations) + + ",notifications=" + Arrays.toString(notifications) + + "]"; + return string; + } + +} diff --git a/libjava/classpath/javax/management/MBeanNotificationInfo.java b/libjava/classpath/javax/management/MBeanNotificationInfo.java new file mode 100644 index 00000000000..ba4077e8fba --- /dev/null +++ b/libjava/classpath/javax/management/MBeanNotificationInfo.java @@ -0,0 +1,227 @@ +/* MBeanNotificationInfo.java -- Information about a bean's notification. + 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 javax.management; + +import java.util.Arrays; + +/** + * <p> + * Describes the notifications emitted by a management bean. + * An instance of this class is specific to notifications + * involving a particular type of object. A new instance + * should be created for each Java class used for notifications, + * and the Java class name forms the name of the instance. + * Each instance lists a number of notification types; these + * are not types in the sense of different Java classes, but + * instead form the names of notifications following the same + * syntax as Java property and package names. + * </p> + * <p> + * For instance, a management bean may emit two notifications + * containing {@link java.lang.String} objects. Both would be described + * using one instance of this class, with a member of the array + * returned by {@link #getNotifTypes()} for each one. If another + * notification containing a {@link java.util.Date} object were to + * be added, this would require a new instance of this class. + * </p> + * <p> + * The information in this class is immutable as standard. + * Of course, subclasses may change this, but this + * behaviour is not recommended. + * </p> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MBeanNotificationInfo + extends MBeanFeatureInfo + implements Cloneable +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = -3888371564530107064L; + + /** + * The types of notification described by this instance. + * + * @serial the types of notification. + */ + private String[] types; + + /** + * Constructs a new {@link MBeanNotificationInfo} with the + * specified name, description and notification types. The + * notification types array may be <code>null</code> or of + * zero length, in order to indicate the absence of any types. + * + * @param types an array of {@link java.lang.String} objects, + * containing the names of the notifications emitted + * of this Java type. The names use the dot notation + * familiar from Java property and package names. + * @param name the name of the Java class the notifications described + * by this object are instances of. + * @param description a description of the data. + * @throws IllegalArgumentException for some reason... + */ + public MBeanNotificationInfo(String[] types, String name, + String description) + { + super(name, description); + this.types = types; + } + + /** + * Returns a clone of this instance. The clone is created + * using just the method provided by {@link java.lang.Object}. + * Thus, the clone is just a shallow clone as returned by + * that method, and does not contain any deeper cloning based + * on the subject of this class. + * + * @return a clone of this instance. + * @see java.lang.Cloneable + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + /* This shouldn't happen; we implement Cloneable */ + throw new IllegalStateException("clone() called on " + + "non-cloneable object."); + } + } + + /** + * Compares this feature with the supplied object. This returns + * true iff the object is an instance of {@link + * MBeanNotificationInfo}, {@link Object#equals()} returns true for + * a comparison of both the name and description of this + * notification with that of the specified object, and the two + * notification type arrays contain the same elements in the same + * order (but one may be longer than the other). + * + * @param obj the object to compare. + * @return true if the object is a {@link MBeanNotificationInfo} + * instance, + * <code>name.equals(object.getName())</code>, + * <code>description.equals(object.getDescription())</code> + * and the corresponding elements of the type arrays are + * equal. + */ + public boolean equals(Object obj) + { + if (obj instanceof MBeanNotificationInfo) + { + if (!(super.equals(obj))) + return false; + MBeanNotificationInfo o = (MBeanNotificationInfo) obj; + String[] oTypes = o.getNotifTypes(); + for (int a = 0; a < types.length; ++a) + { + if (a == oTypes.length) + return true; + if (!(types[a].equals(oTypes[a]))) + return false; + } + return true; + } + else + return false; + } + + /** + * Returns the notification types that the management bean may emit. + * The notification types are strings using the dot notation + * familiar from Java property and package names. Changing the + * returned array does not affect the values retained by this + * instance. + * + * @return the notification types. + */ + public String[] getNotifTypes() + { + return types; + } + + /** + * Returns the hashcode of the notification information as the sum + * of the hashcode of the superclass and the hashcode of the types + * array. + * + * @return the hashcode of the notification information. + */ + public int hashCode() + { + return super.hashCode() + Arrays.hashCode(types); + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.MBeanNotificationInfo</code>), + * the name and description of the notification and the + * contents of the array of types. + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + { + super.toString(); + string = string.substring(0, string.length() - 1) + + ",types=" + Arrays.toString(types) + + "]"; + } + return string; + } + +} diff --git a/libjava/classpath/javax/management/MBeanOperationInfo.java b/libjava/classpath/javax/management/MBeanOperationInfo.java new file mode 100644 index 00000000000..a2db8d1b064 --- /dev/null +++ b/libjava/classpath/javax/management/MBeanOperationInfo.java @@ -0,0 +1,344 @@ +/* MBeanOperationInfo.java -- Information about a bean's operations. + 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 javax.management; + +import java.lang.reflect.Method; + +import java.util.Arrays; + +/** + * Describes the operations of a management bean. + * The information in this class is immutable as standard. + * Of course, subclasses may change this, but this + * behaviour is not recommended. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MBeanOperationInfo + extends MBeanFeatureInfo + implements Cloneable +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = -6178860474881375330L; + + /** + * Used to signify that the operation merely provides information + * (akin to an accessor). + */ + public static final int INFO = 0; + + /** + * Used to signify that the operation makes some change to the + * state of the bean (akin to a mutator). + */ + public static final int ACTION = 1; + + /** + * Used to signify that the operation makes some state change + * to the bean and also returns information. + */ + public static final int ACTION_INFO = 2; + + /** + * Used to signify that the behaviour of the operation is + * unknown. + */ + public static final int UNKNOWN = 3; + + /** + * The return type of the method, in the form of its class name. + */ + private String type; + + /** + * The signature of the constructor i.e. the argument types. + */ + private MBeanParameterInfo[] signature; + + /** + * The impact of the method, as one of {@link #INFO}, {@link #ACTION}, + * {@link #ACTION_INFO} and {@link #UNKNOWN}. + */ + private int impact; + + /** + * Constructs a @link{MBeanOperationInfo} with the specified + * description using the given method. Each parameter is + * described merely by its type; the name and description are + * <code>null</code>. The return type and impact of the + * method are determined from the {@link Method} instance. + * + * @param desc a description of the attribute. + * @param method the method. + */ + public MBeanOperationInfo(String desc, Method method) + { + super(method.getName(), desc); + Class[] paramTypes = method.getParameterTypes(); + signature = new MBeanParameterInfo[paramTypes.length]; + for (int a = 0; a < paramTypes.length; ++a) + signature[a] = new MBeanParameterInfo(null, + paramTypes[a].getName(), + null); + type = method.getReturnType().getName(); + if (method.getReturnType() == Void.TYPE) + { + if (paramTypes.length == 0) + impact = UNKNOWN; + else + impact = ACTION; + } + else + { + if (paramTypes.length == 0) + impact = INFO; + else + impact = ACTION_INFO; + } + } + + /** + * Constructs a @link{MBeanOperationInfo} with the specified name, + * description, parameter information, return type and impact. A + * <code>null</code> value for the parameter information is the same + * as passing in an empty array. + * + * @param name the name of the constructor. + * @param desc a description of the attribute. + * @param sig the signature of the method, as a series + * of {@link MBeanParameterInfo} objects, one for + * each parameter. + * @param type the return type of the method, as the class name. + * @param impact the impact of performing the operation. + */ + public MBeanOperationInfo(String name, String desc, + MBeanParameterInfo[] sig, String type, + int impact) + { + super(name, desc); + if (sig == null) + signature = new MBeanParameterInfo[0]; + else + signature = sig; + this.type = type; + this.impact = impact; + } + + /** + * Returns a clone of this instance. The clone is created + * using just the method provided by {@link java.lang.Object}. + * Thus, the clone is just a shallow clone as returned by + * that method, and does not contain any deeper cloning based + * on the subject of this class. + * + * @return a clone of this instance. + * @see java.lang.Cloneable + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + /* This shouldn't happen; we implement Cloneable */ + throw new IllegalStateException("clone() called on " + + "non-cloneable object."); + } + } + + /** + * Compares this feature with the supplied object. This returns + * true iff the object is an instance of {@link + * MBeanConstructorInfo}, {@link Object#equals()} returns true for a + * comparison of both the name and description of this notification + * with that of the specified object (performed by the superclass), + * the return type and impact are equal and the two signature arrays + * contain the same elements in the same order (but one may be + * longer than the other). + * + * @param obj the object to compare. + * @return true if the object is a {@link MBeanOperationInfo} + * instance, + * <code>name.equals(object.getName())</code>, + * <code>description.equals(object.getDescription())</code>, + * <code>type.equals(object.getReturnType())</code>, + * <code>impact == object.getImpact()</code>, + * and the corresponding elements of the signature arrays are + * equal. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof MBeanOperationInfo)) + return false; + if (!(super.equals(obj))) + return false; + MBeanOperationInfo o = (MBeanOperationInfo) obj; + MBeanParameterInfo[] sig = o.getSignature(); + for (int a = 0; a < signature.length; ++a) + { + if (a == sig.length) + return true; + if (!(signature[a].equals(sig[a]))) + return false; + } + return (type.equals(o.getReturnType()) && + impact == o.getImpact()); + } + + /** + * <p> + * Returns the impact of performing this operation. + * The value is equal to one of the following: + * </p> + * <ol> + * <li>{@link #INFO} — the method just returns + * information (akin to an accessor).</li> + * <li>{@link #ACTION} — the method just alters + * the state of the bean, without returning a value + * (akin to a mutator).</li> + * <li>{@link #ACTION_INFO} — the method both makes + * state changes and returns a value.</li> + * <li>{@link #UNKNOWN} — the behaviour of the operation + * is unknown.</li> + * </ol> + * + * @return the impact of performing the operation. + */ + public int getImpact() + { + return impact; + } + + /** + * Returns the return type of the operation, as the class + * name. + * + * @return the return type. + */ + public String getReturnType() + { + return type; + } + + /** + * Returns the operation's signature, in the form of + * information on each parameter. Each parameter is + * described by an instance of {@link MBeanParameterInfo}. + * The returned array is a shallow copy of the array used + * by this instance, so changing which elements are stored + * in the array won't affect the array used by this, but + * changing the actual elements will affect the ones used + * here. + * + * @return an array of {@link MBeanParameterInfo} objects, + * describing the operation parameters. + */ + public MBeanParameterInfo[] getSignature() + { + return (MBeanParameterInfo[]) signature.clone(); + } + + /** + * Returns the hashcode of the operation information as the sum of + * the hashcode of the superclass, the parameter array, the return + * type and the impact factor. + * + * @return the hashcode of the operation information. + */ + public int hashCode() + { + return super.hashCode() + Arrays.hashCode(signature) + + type.hashCode() + Integer.valueOf(impact).hashCode(); + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.MBeanOperationInfo</code>), + * the name, description, return type and impact of the + * operation and the contents of the array of parameters. + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + { + String impactString; + switch (impact) + { + case INFO: + impactString = "INFO"; + break; + case ACTION: + impactString = "ACTION"; + break; + case ACTION_INFO: + impactString = "ACTION_INFO"; + break; + case UNKNOWN: + impactString = "UNKNOWN"; + break; + default: + impactString = "ERRONEOUS VALUE"; + } + super.toString(); + string = string.substring(0, string.length() - 1) + + ",returnType=" + type + + ",impact=" + impactString + + ",signature=" + Arrays.toString(signature) + + "]"; + } + return string; + } + +} diff --git a/libjava/classpath/javax/management/MBeanParameterInfo.java b/libjava/classpath/javax/management/MBeanParameterInfo.java new file mode 100644 index 00000000000..ee6ef99b843 --- /dev/null +++ b/libjava/classpath/javax/management/MBeanParameterInfo.java @@ -0,0 +1,176 @@ +/* MBeanParameterInfo.java -- Information about an operation's parameters. + 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 javax.management; + +/** + * Describes the parameters of a constructor or operation associated + * with a management bean. The information in this class is immutable + * as standard. Of course, subclasses may change this, but this + * behaviour is not recommended. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MBeanParameterInfo + extends MBeanFeatureInfo + implements Cloneable +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 7432616882776782338L; + + /** + * The type of the parameter, represented by the class name. + */ + private String type; + + /** + * Constructs a new {@link MBeanParameterInfo} using the specified + * name, description and type. + * + * @param name the name of the attribute. + * @param type the type of the attribute, in the form of its class name. + * @param desc a description of the attribute. + */ + public MBeanParameterInfo(String name, String type, String desc) + { + super(name, desc); + this.type = type; + } + + /** + * Returns a clone of this instance. The clone is created + * using just the method provided by {@link java.lang.Object}. + * Thus, the clone is just a shallow clone as returned by + * that method, and does not contain any deeper cloning based + * on the subject of this class. + * + * @return a clone of this instance. + * @see java.lang.Cloneable + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + /* This shouldn't happen; we implement Cloneable */ + throw new IllegalStateException("clone() called on " + + "non-cloneable object."); + } + } + + /** + * Compares this feature with the supplied object. This returns + * true iff the object is an instance of {@link MBeanParameterInfo}, + * {@link Object#equals()} returns true for a comparison of both the + * name and description of this parameter with that of the specified + * object (performed by the superclass), and the type of the two + * instances is equal. + * + * @param obj the object to compare. + * @return true if the object is a {@link MBeanParameterInfo} + * instance, + * <code>name.equals(object.getName())</code>, + * <code>description.equals(object.getDescription())</code>, + * and <code>type.equals(object.getType())</code>. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof MBeanParameterInfo)) + return false; + if (!(super.equals(obj))) + return false; + MBeanParameterInfo o = (MBeanParameterInfo) obj; + return type.equals(o.getType()); + } + + /** + * Returns the type of this attribute, in the form of its class name. + * + * @return the type of this attribute. + */ + public String getType() + { + return type; + } + + /** + * Returns the hashcode of the parameter information as the sum of + * the hashcode of the superclass and the hashcode of the type. + * + * @return the hashcode of the parameter information. + */ + public int hashCode() + { + return super.hashCode() + type.hashCode(); + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.MBeanParameterInfo</code>) along + * with the name, description and type of the parameter. + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + { + super.toString(); + string = string.substring(0, string.length() - 1) + + ",type=" + type + + "]"; + } + return string; + } + +} diff --git a/libjava/classpath/javax/management/NotCompliantMBeanException.java b/libjava/classpath/javax/management/NotCompliantMBeanException.java new file mode 100644 index 00000000000..f2252f28b3a --- /dev/null +++ b/libjava/classpath/javax/management/NotCompliantMBeanException.java @@ -0,0 +1,78 @@ +/* NotCompliantMBeanException.java -- Thrown due to a non-compliant bean. + 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 javax.management; + +/** + * Thrown when a management bean is passed to a method + * (e.g. to an MBean server to be registered) and it + * fails to comply with the specifications for such + * a bean. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class NotCompliantMBeanException + extends OperationsException +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 5175579583207963577L; + + /** + * Constructs a new <code>NotCompliantMBeanException</code>. + */ + public NotCompliantMBeanException() + { + super(); + } + + /** + * Constructs a new <code>NotCompliantMBeanException</code> + * with the specified message. + * + * @param message the error message to give to the user. + */ + public NotCompliantMBeanException(String message) + { + super(message); + } + +} + diff --git a/libjava/classpath/javax/management/Notification.java b/libjava/classpath/javax/management/Notification.java new file mode 100644 index 00000000000..52c11de06af --- /dev/null +++ b/libjava/classpath/javax/management/Notification.java @@ -0,0 +1,314 @@ +/* Notification.java -- A notification emitted by a bean. + 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 javax.management; + +import java.util.Date; +import java.util.EventObject; + +/** + * <p> + * A notification message that may be emitted by a bean. + * Notifications have both a message and a type, so individual + * notifications can be grouped by type. They also incorporate + * sequencing, so that the recipient can order the delivered + * messages correctly (there is no guarantee that they will + * be delivered in order). + * </p> + * <p> + * Notifications also include a reference to the source of + * the notification. The source bean is represented either + * by an {@link ObjectName} or by a direct reference to the + * bean. The former is preferable, and notifications emitted + * via a {@link MBeanServer} will automatically have the source + * transformed into an {@link ObjectName}. + * </p> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class Notification + extends EventObject +{ + + /** + * The notification message. + * + * @serial the notification message. + */ + private String message; + + /** + * The notification's sequence number, relative to the notifications + * emitted by the bean. + * + * @serial the notification sequence number. + */ + private long sequenceNumber; + + /** + * The source of the notification. This is redeclared in order to + * replace the <code>source</code> variable in {@link java.util.EventObject} + * with a non-transient version. + * + * @serial the notification source. + */ + protected Object source; + + /** + * The time the notification was generated. + * + * @serial the notification timestamp. + */ + private long timeStamp; + + /** + * The type of notification sent. This utilises the same style + * as Java property and package names. For example, + * <code>gnu.gcj.compiler</code> may be one type of notifications. + * + * @serial the notification type. + */ + private String type; + + /** + * The user data associated with the notification. This includes + * any additional data which should be transmitted with the notification, + * but can't be achieved using the {@link java.lang.String} format + * of the <code>message</code>. + * + * @serial the notification user data. + */ + private Object userData; + + /** + * Creates a new {@link Notification} object with the specified type, + * source and sequence number. The timestamp is created using the + * current date and time. + * + * @param type the type of the notification. + * @param source the source of the notification. + * @param sequenceNumber the sequence number of the notifcation. + */ + public Notification(String type, Object source, long sequenceNumber) + { + this(type, source, sequenceNumber, new Date().getTime()); + } + + /** + * Creates a new {@link Notification} object with the specified type, + * source, sequence number and timestamp. + * + * @param type the type of the notification. + * @param source the source of the notification. + * @param sequenceNumber the sequence number of the notifcation. + * @param timeStamp the time the notification was emitted. + */ + public Notification(String type, Object source, long sequenceNumber, + long timeStamp) + { + this(type, source, sequenceNumber, timeStamp, null); + } + + /** + * Creates a new {@link Notification} object with the specified type, + * source, sequence number, timestamp and message. + * + * @param type the type of the notification. + * @param source the source of the notification. + * @param sequenceNumber the sequence number of the notifcation. + * @param timeStamp the time the notification was emitted. + * @param message the message contained in the notification. + */ + public Notification(String type, Object source, long sequenceNumber, + long timeStamp, String message) + { + super(source); + this.type = type; + this.sequenceNumber = sequenceNumber; + this.timeStamp = timeStamp; + this.message = message; + } + + /** + * Creates a new {@link Notification} object with the specified type, + * source, sequence number and message. The timestamp is created using + * the current date and time. + * + * @param type the type of the notification. + * @param source the source of the notification. + * @param sequenceNumber the sequence number of the notifcation. + * @param message the message contained in the notification. + */ + public Notification(String type, Object source, long sequenceNumber, + String message) + { + this(type, source, sequenceNumber, new Date().getTime(), message); + } + + /** + * Returns the message contained in this notification. The message + * is in {@link java.lang.String} form, and is thus intended for + * display to the end-user. Data transferred as part of the notification + * which shouldn't be displayed is included in the <code>userData</code> + * field. + * + * @return the notification message. + * @see #getUserData() + * @see #setUserData(java.lang.Object) + */ + public String getMessage() + { + return message; + } + + /** + * Returns the sequence number of this notification. This + * can be used to determine the order in which notifications + * were emitted by the broadcasting bean. + * + * @return the sequence number. + * @see #setSequenceNumber(long) + */ + public long getSequenceNumber() + { + return sequenceNumber; + } + + /** + * Returns the date and time at which this notification was + * emitted. + * + * @return the notification timestamp. + * @see #setTimeStamp(long) + */ + public long getTimeStamp() + { + return timeStamp; + } + + /** + * Returns the type of this notification. Types take the same + * form as Java package and property names. + * + * @return the type of the notification. + */ + public String getType() + { + return type; + } + + /** + * Returns the additional user data associated with the notification. + * This is used to attach additional non-textual information to the + * notification. + * + * @return the user data associated with the notification. + * @see #setUserData(java.lang.Object) + */ + public Object getUserData() + { + return userData; + } + + /** + * Sets the sequence number to the value specified. + * + * @param sequenceNumber the new sequence number. + * @see #getSequenceNumber() + */ + public void setSequenceNumber(long sequenceNumber) + { + this.sequenceNumber = sequenceNumber; + } + + /** + * Sets the source of this notification to the value + * specified. + * + * @param source the new source of the notification. + * @see java.util.EventSource#getSource() + */ + public void setSource(Object source) + { + this.source = source; + } + + /** + * Sets the date and time at which this notification + * was emitted. + * + * @param timeStamp the new time stamp of the notification. + * @see #getTimeStamp() + */ + public void setTimeStamp(long timeStamp) + { + this.timeStamp = timeStamp; + } + + /** + * Sets the additional user data associated with the notification + * to the specified value. This is used to attach additional + * non-textual information to the notification. + * + * @param userData the new user data associated with the notification. + * @see #getUserData() + */ + public void setUserData(Object userData) + { + this.userData = userData; + } + + /** + * A textual representation of the notification. + * + * @return the notification in {@link java.lang.String} form. + */ + public String toString() + { + return getClass().getName() + + "[message=" + message + + ", sequenceNumber=" + sequenceNumber + + ", source=" + source + + ", timeStamp=" + timeStamp + + ", type=" + type + + ", userData=" + userData + + "]"; + } + +} + diff --git a/libjava/classpath/javax/management/NotificationBroadcaster.java b/libjava/classpath/javax/management/NotificationBroadcaster.java new file mode 100644 index 00000000000..139d842bb41 --- /dev/null +++ b/libjava/classpath/javax/management/NotificationBroadcaster.java @@ -0,0 +1,112 @@ +/* NotificationBroadcaster.java -- Interface for broadcasters. + 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 javax.management; + +/** + * <p> + * Represents a bean that can emit notifications when + * events occur. Other beans can use this interface + * to add themselves to the list of recipients of such + * notifications. + * </p> + * <p> + * <strong>Note</strong>: New classes should use + * {@link NotificationEmitter}, a subinterface of this, + * in preference to using this interface directly. + * </p> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface NotificationBroadcaster +{ + + /** + * Registers the specified listener as a new recipient of + * notifications from this bean. If non-null, the filter + * argument will be used to select which notifications are + * delivered. The supplied object will also be passed to + * the recipient with each notification. This should not + * be modified by the broadcaster, but instead should be + * passed unmodified to the listener. + * + * @param listener the new listener, who will receive + * notifications from this broadcasting bean. + * @param filter a filter to determine which notifications are + * delivered to the listener, or <code>null</code> + * if no filtering is required. + * @param passback an object to be passed to the listener with + * each notification. + * @throws IllegalArgumentException if <code>listener</code> is + * <code>null</code>. + * @see #removeNotificationListener(NotificationListener) + */ + void addNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object passback) + throws IllegalArgumentException; + + /** + * Returns an array describing the notifications this + * bean may send to its registered listeners. Ideally, this + * array should be complete, but in some cases, this may + * not be possible. However, be aware that some listeners + * may expect this to be so. + * + * @return the array of possible notifications. + */ + MBeanNotificationInfo[] getNotificationInfo(); + + /** + * Removes the specified listener from the list of recipients + * of notifications from this bean. This includes all combinations + * of filters and passback objects registered for this listener. + * For more specific removal of listeners, see the subinterface + * {@link NotificationEmitter}. + * + * @param listener the listener to remove. + * @throws ListenerNotFoundException if the specified listener + * is not registered with this bean. + * @see #addNotificationListener(NotificationListener, NotificationFilter, + * java.lang.Object) + */ + void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException; + +} + diff --git a/libjava/classpath/javax/management/NotificationEmitter.java b/libjava/classpath/javax/management/NotificationEmitter.java new file mode 100644 index 00000000000..002e2fe1890 --- /dev/null +++ b/libjava/classpath/javax/management/NotificationEmitter.java @@ -0,0 +1,76 @@ +/* NotificationEmitter.java -- Refined interface for broadcasters. + 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 javax.management; + +/** + * Represents a bean that can emit notifications when + * events occur. Other beans can use this interface + * to add themselves to the list of recipients of such + * notifications. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface NotificationEmitter + extends NotificationBroadcaster +{ + + /** + * Removes the specified listener from the list of recipients + * of notifications from this bean. Only the first instance with + * the supplied filter and passback object is removed. + * <code>null</code> is used as a valid value for these parameters, + * rather than as a way to remove all registration instances for + * the specified listener; for this behaviour instead, see the details + * of the same method in {@link NotificationBroadcaster}. + * + * @param listener the listener to remove. + * @param filter the filter of the listener to remove. + * @param passback the passback object of the listener to remove. + * @throws ListenerNotFoundException if the specified listener + * is not registered with this bean. + * @see #addNotificationListener(NotificationListener, NotificationFilter, + * java.lang.Object) + */ + void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object passback) + throws ListenerNotFoundException; + +} + diff --git a/libjava/classpath/javax/management/NotificationFilter.java b/libjava/classpath/javax/management/NotificationFilter.java new file mode 100644 index 00000000000..a8e41c93fbc --- /dev/null +++ b/libjava/classpath/javax/management/NotificationFilter.java @@ -0,0 +1,66 @@ +/* NotificationFilter.java -- Interface for notification filters. + 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 javax.management; + +import java.io.Serializable; + +/** + * Represents a object that acts as a filter for notifications. + * Implementations of this class are used to determine which + * notifications should be passed to a receiving bean, and which + * should not. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface NotificationFilter + extends Serializable +{ + + /** + * Returns true if the specified notification should be passed + * on to the listener. + * + * @param notification the notification being delivered. + * @return true if the notification should be passed to the + * listener, false otherwise. + */ + boolean isNotificationEnabled(Notification notification); + +} + diff --git a/libjava/classpath/javax/management/NotificationListener.java b/libjava/classpath/javax/management/NotificationListener.java new file mode 100644 index 00000000000..69b08eee3b3 --- /dev/null +++ b/libjava/classpath/javax/management/NotificationListener.java @@ -0,0 +1,70 @@ +/* NotificationListener.java -- Interface for receivers of notifications. + 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 javax.management; + +import java.util.EventListener; + +/** + * Represents a object that can receive notifications from + * a bean. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface NotificationListener + extends EventListener +{ + + /** + * Invoked by the notifying bean when a notification is to + * be delivered to the recipient. As the transmission of + * notifications takes place sequentially, implementors of + * this method should avoid performing lengthy operations, + * as the notifying bean will stall until the method is + * complete. + * + * @param notification the notification from the bean. + * @param passback the object that was passed to the notifying + * bean as part of the registration process. + * @see NotificationBroadcaster#addListener(NotificationListener, + * NotificationFilter, Object) + */ + void handleNotification(Notification notification, Object passback); + +} + diff --git a/libjava/classpath/javax/management/OperationsException.java b/libjava/classpath/javax/management/OperationsException.java new file mode 100644 index 00000000000..cbd90d63782 --- /dev/null +++ b/libjava/classpath/javax/management/OperationsException.java @@ -0,0 +1,76 @@ +/* OperationsException.java -- Thrown by management operations. + 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 javax.management; + +/** + * A general superclass for all exceptions thrown by + * operations on management beans. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class OperationsException + extends JMException +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = -4967597595580536216L; + + /** + * Constructs a new <code>OperationsException</code>. + */ + public OperationsException() + { + super(); + } + + /** + * Constructs a new <code>OperationsException</code> + * with the specified message. + * + * @param message the error message to give to the user. + */ + public OperationsException(String message) + { + super(message); + } + +} + diff --git a/libjava/classpath/javax/management/ReflectionException.java b/libjava/classpath/javax/management/ReflectionException.java new file mode 100644 index 00000000000..0f19b5b349f --- /dev/null +++ b/libjava/classpath/javax/management/ReflectionException.java @@ -0,0 +1,118 @@ +/* ReflectionException.java -- A reflection-based management exception. + 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 javax.management; + +/** + * Represents one of the reflection exceptions thrown by a + * management bean. When a management bean tries to perform + * a lookup using the reflection API and encounters an exception, + * it gets wrapped inside an {@link ReflectionException}. + * Calling {@link getTargetException()} will return the wrapped + * exception. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class ReflectionException + extends JMException +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 9170809325636915553L; + + /* Sun re-implemented causality -- don't know why, but + serialization demands we do too... */ + + /** + * The target exception. + * + * @serial the target exception. + */ + private Exception exception; + + /** + * Constructs a new <code>ReflectionException</code> wrapping + * the specified exception. + * + * @param e the exception to be wrapped. + */ + public ReflectionException(Exception e) + { + super(); + exception = e; + } + + /** + * Constructs a new <code>ReflectionException</code> wrapping + * the specified exception and using the supplied message. + * + * @param e the exception to be wrapped. + * @param message the error message to give to the user. + */ + public ReflectionException(Exception e, String message) + { + super(message); + exception = e; + } + + /** + * Returns the true cause of this exception, the wrapped + * exception. + * + * @return the wrapped exception. + */ + public Throwable getCause() + { + return exception; + } + + /** + * Returns the true cause of this exception, the wrapped + * exception. + * + * @return the wrapped exception. + */ + public Exception getTargetException() + { + return exception; + } + +} + diff --git a/libjava/classpath/javax/management/RuntimeOperationsException.java b/libjava/classpath/javax/management/RuntimeOperationsException.java new file mode 100644 index 00000000000..8a830a5e708 --- /dev/null +++ b/libjava/classpath/javax/management/RuntimeOperationsException.java @@ -0,0 +1,121 @@ +/* RuntimeOperationsException.java -- A wrapped run-time exception. + 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 javax.management; + +/** + * Represents a runtime exception thrown by a management + * bean operation. When a management bean executes code + * that causes a runtime exception to be thrown, the + * resulting exception is wrapped inside an + * {@link RuntimeOperationsException}. Calling + * {@link getTargetException()} will return the wrapped + * exception. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class RuntimeOperationsException + extends JMRuntimeException +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = -8408923047489133588L; + + /* Sun re-implemented causality -- don't know why, but + serialization demands we do too... */ + + /** + * The target exception. + * + * @serial the target exception. + */ + private RuntimeException runtimeException; + + /** + * Constructs a new <code>RuntimeOperationsException</code> + * wrapping the specified exception. + * + * @param e the exception to be wrapped. + */ + public RuntimeOperationsException(RuntimeException e) + { + super(); + runtimeException = e; + } + + /** + * Constructs a new <code>RuntimeOperationsException</code> + * wrapping the specified exception and using the supplied + * message. + * + * @param e the exception to be wrapped. + * @param message the error message to give to the user. + */ + public RuntimeOperationsException(RuntimeException e, + String message) + { + super(message); + runtimeException = e; + } + + /** + * Returns the true cause of this exception, the wrapped + * exception. + * + * @return the wrapped exception. + */ + public Throwable getCause() + { + return runtimeException; + } + + /** + * Returns the true cause of this exception, the wrapped + * exception. + * + * @return the wrapped exception. + */ + public RuntimeException getTargetException() + { + return runtimeException; + } + +} + diff --git a/libjava/classpath/javax/management/StandardMBean.java b/libjava/classpath/javax/management/StandardMBean.java new file mode 100644 index 00000000000..736192ee2ac --- /dev/null +++ b/libjava/classpath/javax/management/StandardMBean.java @@ -0,0 +1,925 @@ +/* StandardMBean.java -- A standard reflection-based management bean. + 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 javax.management; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Provides a dynamic management bean by using reflection on an + * interface and an implementing class. By default, a bean instance + * is paired up with its interface based on specific naming + * conventions (if the implementation is called X, the interface must + * be XMBean). Using this class removes the need to use a specific + * naming system to match up the two. Instead, an instance of this + * bean is created either via explicit construction or subclassing, + * and this provides access to the attributes, constructors and + * operations of the implementation via reflection. Various hooks are + * provided in order to allow customization of this process. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class StandardMBean + implements DynamicMBean +{ + + /** + * The interface for this bean. + */ + private Class iface; + + /** + * The implementation of the interface. + */ + private Object impl; + + /** + * Cached bean information. + */ + private MBeanInfo info; + + /** + * Constructs a new {@link StandardMBean} using the specified + * interface and <code>this</code> as the instance. This should + * be used to create an instance via subclassing. + * + * @param iface the interface this bean implements, or <code>null</code> + * if the interface should be determined using the naming + * convention (class X has interface XMBean). + * @throws NotCompliantMBeanException if this class doesn't implement + * the interface or a method appears + * in the interface that doesn't comply + * with the naming conventions. + */ + protected StandardMBean(Class iface) + throws NotCompliantMBeanException + { + if (iface == null) + { + String className = getClass().getName(); + try + { + iface = Class.forName(className + "MBean"); + } + catch (ClassNotFoundException e) + { + throw (NotCompliantMBeanException) + (new NotCompliantMBeanException("An interface for the class " + + className + " was not found.").initCause(e)); + } + } + if (!(iface.isInstance(this))) + throw new NotCompliantMBeanException("The instance, " + impl + + ", is not an instance of " + iface); + impl = this; + this.iface = iface; + } + + /** + * Constructs a new {@link StandardMBean} using the specified + * interface and the supplied instance as the implementation. + * + * @param impl the implementation. + * @param iface the interface the bean implements, or <code>null</code> + * if the interface should be determined using the naming + * convention (class X has interface XMBean). + * @throws IllegalArgumentException if <code>impl</code> is <code>null</code>. + * @throws NotCompliantMBeanException if <code>impl</code> doesn't implement + * the interface or a method appears + * in the interface that doesn't comply + * with the naming conventions. + */ + public StandardMBean(Object impl, Class iface) + throws NotCompliantMBeanException + { + if (impl == null) + throw new IllegalArgumentException("The specified implementation is null."); + if (iface == null) + { + String className = impl.getClass().getName(); + try + { + iface = Class.forName(className + "MBean"); + } + catch (ClassNotFoundException e) + { + throw (NotCompliantMBeanException) + (new NotCompliantMBeanException("An interface for the class " + + className + " was not found.").initCause(e)); + } + } + if (!(iface.isInstance(impl))) + throw new NotCompliantMBeanException("The instance, " + impl + + ", is not an instance of " + iface); + this.impl = impl; + this.iface = iface; + } + + /** + * Caches the {@link MBeanInfo} instance for this object. This is a + * customization hook, so that subclasses can choose the caching policy + * used. The default implementation caches the value in the instance + * itself. Subclasses may override this so as to not cache the data + * at all, or so as to use a cache shared between multiple beans. + * + * @param info the {@link MBeanInfo} instance to cache, or <code>null</code> + * if there is no new value to cache. When the value is not + * <code>null</code>, the cache should replace the current value + * with the value supplied here. + * @see #getCachedMBeanInfo() + */ + protected void cacheMBeanInfo(MBeanInfo info) + { + if (info != null) + this.info = info; + } + + /** + * Obtains the value of the specified attribute of the + * management bean. The management bean should perform + * a lookup for the named attribute, and return its value + * by calling the appropriate getter method, if possible. + * + * @param name the name of the attribute to retrieve. + * @return the value of the specified attribute. + * @throws AttributeNotFoundException if the name does not + * correspond to an attribute + * of the bean. + * @throws MBeanException if retrieving the attribute causes + * the bean to throw an exception (which + * becomes the cause of this exception). + * @throws ReflectionException if an exception occurred in trying + * to use the reflection interface + * to lookup the attribute. The + * thrown exception is the cause of + * this exception. + * @see #setAttribute(String) + */ + public Object getAttribute(String name) + throws AttributeNotFoundException, MBeanException, + ReflectionException + { + Method getter; + try + { + getter = iface.getMethod("get" + + name.substring(0, 1).toUpperCase() + + name.substring(1), null); + } + catch (NoSuchMethodException e) + { + try + { + getter = iface.getMethod("is" + + name.substring(0, 1).toUpperCase() + + name.substring(1), null); + } + catch (NoSuchMethodException ex) + { + throw ((AttributeNotFoundException) + new AttributeNotFoundException("The attribute, " + name + + ", was not found.").initCause(ex)); + } + } + Object result; + try + { + result = getter.invoke(impl, null); + } + catch (IllegalAccessException e) + { + throw new ReflectionException(e, "Failed to retrieve " + name); + } + catch (IllegalArgumentException e) + { + throw new ReflectionException(e, "Failed to retrieve " + name); + } + catch (InvocationTargetException e) + { + throw new MBeanException((Exception) e.getCause(), + "The getter of " + name + + " threw an exception"); + } + return result; + } + + /** + * Obtains the values of each of the specified attributes + * of the management bean. The returned list includes + * those attributes that were retrieved and their + * corresponding values. + * + * @param names the names of the attributes to retrieve. + * @return a list of the retrieved attributes. + * @see #setAttributes(AttributeList) + */ + public AttributeList getAttributes(String[] names) + { + AttributeList list = new AttributeList(names.length); + for (int a = 0; a < names.length; ++a) + { + try + { + Object value = getAttribute(names[a]); + list.add(new Attribute(names[a], value)); + } + catch (AttributeNotFoundException e) + { + /* Ignored */ + } + catch (ReflectionException e) + { + /* Ignored */ + } + catch (MBeanException e) + { + /* Ignored */ + } + } + return list; + } + + /** + * Returns the cached {@link MBeanInfo} instance for this object. This is a + * customization hook, so that subclasses can choose the caching policy + * used. The default implementation caches the value in the instance + * itself, and returns this value on calls to this method. + * + * @return the cached {@link MBeanInfo} instance, or <code>null</code> + * if no value is cached. + * @see #cacheMBeanInfo(javax.management.MBeanInfo) + */ + protected MBeanInfo getCachedMBeanInfo() + { + return info; + } + + /** + * Returns the class name that will be used in the {@link MBeanInfo} + * instance. This is a customization hook, so that subclasses can + * provide a custom class name. By default, this returns the class + * name from the supplied {@link MBeanInfo} instance. + * + * @param info the {@link MBeanInfo} instance constructed via + * reflection. + * @return the class name to use in the instance. + */ + protected String getClassName(MBeanInfo info) + { + return info.getClassName(); + } + + /** + * Returns information on the constructors that will be used in + * the {@link MBeanInfo} instance. This is a customization hook, + * so that subclasses can provide their own information on the + * bean's constructors, if necessary. By default, this method + * returns <code>null</code> unless the implementation supplied + * is either <code>null</code> or <code>this</code>. This default + * implementation prevents the use of + * {@link MBeanServer#createMBean} in cases where the bean is + * not created as a subclass of {@link StandardMBean}. + * + * @param constructors the constructor information created via + * reflection. + * @param impl the implementation, or <code>null</code> if this + * should be ignored. + * @return the constructor information to use. + */ + protected MBeanConstructorInfo[] getConstructors(MBeanConstructorInfo[] + constructors, Object impl) + { + if (impl == null || impl == this) + return constructors; + return null; + } + + /** + * Returns the description of the attribute that will be used in + * the supplied {@link MBeanAttributeInfo} instance. This is a + * customization hook, so that subclasses can provide a custom + * description. By default, this calls + * {@link #getDescription(MBeanFeatureInfo)} with the supplied + * {@link MBeanAttributeInfo} instance. + * + * @param info the {@link MBeanAttributeInfo} instance constructed + * via reflection. + * @return the description to use in the instance. + */ + protected String getDescription(MBeanAttributeInfo info) + { + return getDescription((MBeanFeatureInfo) info); + } + + /** + * Returns the description of the constructor that will be used in + * the supplied {@link MBeanConstructorInfo} instance. This is a + * customization hook, so that subclasses can provide a custom + * description. By default, this calls + * {@link #getDescription(MBeanFeatureInfo)} with the supplied + * {@link MBeanConstructorInfo} instance. + * + * @param info the {@link MBeanConstructorInfo} instance constructed + * via reflection. + * @return the description to use in the instance. + */ + protected String getDescription(MBeanConstructorInfo info) + { + return getDescription((MBeanFeatureInfo) info); + } + + /** + * Returns the description of the nth parameter of the constructor + * that will be used in the supplied {@link MBeanParameterInfo} + * instance. This is a customization hook, so that subclasses + * can provide a custom description. By default, this calls + * <code>param.getDescription()</code>. + * + * @param info the {@link MBeanConstructorInfo} instance constructed + * via reflection. + * @param param the {@link MBeanParameterInfo} instance constructed + * via reflection. + * @param n the number of the parameter, in order to link it to the + * information on the constructor. + * @return the description to use in the instance. + */ + protected String getDescription(MBeanConstructorInfo info, + MBeanParameterInfo param, int n) + { + return param.getDescription(); + } + + /** + * Returns the description of the supplied feature that + * will be used in the supplied {@link MBeanFeatureInfo} + * instance. This is a customization hook, so that subclasses + * can provide a custom description. By default, this calls + * <code>info.getDescription()</code>. This method is also called + * by default for the more specific description methods for attributes, + * constructors and operations. + * + * @param info the {@link MBeanFeatureInfo} instance constructed + * via reflection. + * @return the description to use in the instance. + */ + protected String getDescription(MBeanFeatureInfo info) + { + return info.getDescription(); + } + + /** + * Returns the description of the bean that will be used in the + * supplied {@link MBeanInfo} instance. This is a customization + * hook, so that subclasses can provide a custom description. By + * default, this calls <code>info.getDescription()</code>. + * + * @param info the {@link MBeanInfo} instance constructed + * via reflection. + * @return the description to use in the instance. + */ + protected String getDescription(MBeanInfo info) + { + return info.getDescription(); + } + + /** + * Returns the description of the operation that will be used in + * the supplied {@link MBeanOperationInfo} instance. This is a + * customization hook, so that subclasses can provide a custom + * description. By default, this calls + * {@link #getDescription(MBeanFeatureInfo)} with the supplied + * {@link MBeanOperationInfo} instance. + * + * @param info the {@link MBeanOperationInfo} instance constructed + * via reflection. + * @return the description to use in the instance. + */ + protected String getDescription(MBeanOperationInfo info) + { + return getDescription((MBeanFeatureInfo) info); + } + + /** + * Returns the description of the nth parameter of the operation + * that will be used in the supplied {@link MBeanParameterInfo} + * instance. This is a customization hook, so that subclasses + * can provide a custom description. By default, this calls + * <code>param.getDescription()</code>. + * + * @param info the {@link MBeanOperationInfo} instance constructed + * via reflection. + * @param param the {@link MBeanParameterInfo} instance constructed + * via reflection. + * @param n the number of the parameter, in order to link it to the + * information on the operation. + * @return the description to use in the instance. + */ + protected String getDescription(MBeanOperationInfo info, + MBeanParameterInfo param, int n) + { + return param.getDescription(); + } + + /** + * Returns the impact of the operation that will be used in the + * supplied {@link MBeanOperationInfo} instance. This is a + * customization hook, so that subclasses can provide a custom + * impact flag. By default, this returns + * <code>info.getImpact()</code>. + * + * @param info the {@link MBeanOperationInfo} instance constructed + * via reflection. + * @return the impact flag to use in the instance. + */ + protected int getImpact(MBeanOperationInfo info) + { + return info.getImpact(); + } + + /** + * Returns the instance that implements this bean. + * + * @return the implementation. + */ + public Object getImplementation() + { + return impl; + } + + /** + * Returns the class of the instance that implements this bean. + * + * @return the implementation class. + */ + public Class getImplementationClass() + { + return impl.getClass(); + } + + /** + * <p> + * Returns an information object which lists the attributes + * and actions associated with the management bean. This + * implementation proceeds as follows: + * </p> + * <ol> + * <li>{@link #getCachedMBeanInfo()} is called to obtain + * the cached instance. If this returns a non-null value, + * this value is returned.</li> + * <li>If there is no cached value, then the method proceeds + * to create one. During this process, the customization hooks + * detailed in this class are called to allow the values used + * to be overrided: + * <ul> + * <li>For each attribute, + * {@link #getDescription(MBeanAttributeInfo)} is called.</li> + * <li>For each constructor, + * {@link #getDescription(MBeanConstructorInfo)} is called, + * along with {@link #getDescription(MBeanConstructorInfo, + * MBeanParameterInfo, int)} and + * {@link #getParameterName(MBeanConstructorInfo, + * MBeanParameterInfo, int)} for each parameter.</li> + * <li>The constructors may be replaced as a whole by + * a call to + * {@link #getConstructors(MBeanConstructorInfo[], Object)}.</li> + * <li>For each operation, + * {@link #getDescription(MBeanOperationInfo)} and + * {@link #getImpact(MBeanOperationInfo)} are called, + * along with {@link #getDescription(MBeanOperationInfo, + * MBeanParameterInfo, int)} and + * {@link #getParameterName(MBeanOperationInfo, + * MBeanParameterInfo, int)} for each parameter.</li> + * <li>{@link #getClassName(MBeanInfo)} and + * {@link #getDescription(MBeanInfo)} are called to customise + * the basic information about the class.</li> + * </ul> + * </li> + * <li>Finally, {@link #cacheMBeanInfo(MBeanInfo)} is called + * with the created instance before it is returned.</li> + * </ol> + * + * @return a description of the management bean, including + * all exposed attributes and actions. + */ + public MBeanInfo getMBeanInfo() + { + MBeanInfo info = getCachedMBeanInfo(); + if (info != null) + return info; + Method[] methods = iface.getMethods(); + Map attributes = new HashMap(); + List operations = new ArrayList(); + for (int a = 0; a < methods.length; ++a) + { + String name = methods[a].getName(); + if (((name.startsWith("get") && + methods[a].getReturnType() != Void.TYPE) || + (name.startsWith("is") && + methods[a].getReturnType() == Boolean.TYPE)) && + methods[a].getParameterTypes().length == 0) + { + Method[] amethods; + String attrib; + if (name.startsWith("is")) + attrib = name.substring(2,3).toLowerCase() + + name.substring(3); + else + attrib = name.substring(3,4).toLowerCase() + + name.substring(4); + if (attributes.containsKey(attrib)) + amethods = (Method[]) attributes.get(attrib); + else + { + amethods = new Method[2]; + attributes.put(attrib, amethods); + } + amethods[0] = methods[a]; + } + else if (name.startsWith("set") && + methods[a].getReturnType() == Void.TYPE && + methods[a].getParameterTypes().length == 1) + { + Method[] amethods; + String attrib = name.substring(3,4).toLowerCase() + + name.substring(4); + if (attributes.containsKey(attrib)) + amethods = (Method[]) attributes.get(attrib); + else + { + amethods = new Method[2]; + attributes.put(attrib, amethods); + } + amethods[1] = methods[a]; + } + else + operations.add(new MBeanOperationInfo("", methods[a])); + } + List attribs = new ArrayList(attributes.size()); + Iterator it = attributes.entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry entry = (Map.Entry) it.next(); + Method[] amethods = (Method[]) entry.getValue(); + try + { + attribs.add(new MBeanAttributeInfo((String) entry.getKey(), "", + amethods[0], amethods[1])); + } + catch (IntrospectionException e) + { + /* Shouldn't happen; both shouldn't be null */ + throw new IllegalStateException("The two methods passed to " + + "the MBeanAttributeInfo " + + "constructor for " + entry + + "were null.", e); + } + } + MBeanAttributeInfo[] ainfo = new MBeanAttributeInfo[attribs.size()]; + for (int a = 0; a < ainfo.length; ++a) + { + MBeanAttributeInfo oldInfo = (MBeanAttributeInfo) attribs.get(a); + String desc = getDescription(oldInfo); + ainfo[a] = new MBeanAttributeInfo(oldInfo.getName(), + oldInfo.getType(), desc, + oldInfo.isReadable(), + oldInfo.isWritable(), + oldInfo.isIs()); + } + Constructor[] cons = impl.getClass().getConstructors(); + MBeanConstructorInfo[] cinfo = new MBeanConstructorInfo[cons.length]; + for (int a = 0; a < cinfo.length; ++a) + { + MBeanConstructorInfo oldInfo = new MBeanConstructorInfo("", cons[a]); + String desc = getDescription(oldInfo); + MBeanParameterInfo[] params = oldInfo.getSignature(); + MBeanParameterInfo[] pinfo = new MBeanParameterInfo[params.length]; + for (int b = 0; b < pinfo.length; ++b) + { + String pdesc = getDescription(oldInfo, params[b], b); + String pname = getParameterName(oldInfo, params[b], b); + pinfo[b] = new MBeanParameterInfo(pname, params[b].getType(), + pdesc); + } + cinfo[a] = new MBeanConstructorInfo(oldInfo.getName(), desc, + pinfo); + } + cinfo = getConstructors(cinfo, impl); + MBeanOperationInfo[] oinfo = new MBeanOperationInfo[operations.size()]; + for (int a = 0; a < oinfo.length; ++a) + { + MBeanOperationInfo oldInfo = (MBeanOperationInfo) operations.get(a); + String desc = getDescription(oldInfo); + int impact = getImpact(oldInfo); + MBeanParameterInfo[] params = oldInfo.getSignature(); + MBeanParameterInfo[] pinfo = new MBeanParameterInfo[params.length]; + for (int b = 0; b < pinfo.length; ++b) + { + String pdesc = getDescription(oldInfo, params[b], b); + String pname = getParameterName(oldInfo, params[b], b); + pinfo[b] = new MBeanParameterInfo(pname, params[b].getType(), + pdesc); + } + oinfo[a] = new MBeanOperationInfo(oldInfo.getName(), desc, pinfo, + oldInfo.getReturnType(), impact); + } + info = new MBeanInfo(impl.getClass().getName(), "", ainfo, cinfo, + oinfo, null); + String cname = getClassName(info); + String desc = getDescription(info); + info = new MBeanInfo(cname, desc, ainfo, cinfo, oinfo, null); + cacheMBeanInfo(info); + return info; + } + + /** + * Returns the interface for this management bean. + * + * @return the management interface. + */ + public Class getMBeanInterface() + { + return iface; + } + + /** + * Returns the name of the nth parameter of the constructor + * that will be used in the supplied {@link MBeanParameterInfo} + * instance. This is a customization hook, so that subclasses + * can provide a custom name. By default, this calls + * <code>param.getName()</code>. + * + * @param info the {@link MBeanConstructorInfo} instance constructed + * via reflection. + * @param param the {@link MBeanParameterInfo} instance constructed + * via reflection. + * @param n the number of the parameter, in order to link it to the + * information on the constructor. + * @return the name to use in the instance. + */ + protected String getParameterName(MBeanConstructorInfo info, + MBeanParameterInfo param, int n) + { + return param.getName(); + } + + /** + * Returns the name of the nth parameter of the operation + * that will be used in the supplied {@link MBeanParameterInfo} + * instance. This is a customization hook, so that subclasses + * can provide a custom name. By default, this calls + * <code>param.getName()</code>. + * + * @param info the {@link MBeanOperationInfo} instance constructed + * via reflection. + * @param param the {@link MBeanParameterInfo} instance constructed + * via reflection. + * @param n the number of the parameter, in order to link it to the + * information on the operation. + * @return the name to use in the instance. + */ + protected String getParameterName(MBeanOperationInfo info, + MBeanParameterInfo param, int n) + { + return param.getName(); + } + + /** + * Invokes the specified action on the management bean using + * the supplied parameters. The signature of the action is + * specified by a {@link String} array, which lists the classes + * corresponding to each parameter. The class loader used to + * load these classes is the same as that used for loading the + * management bean itself. + * + * @param name the name of the action to invoke. + * @param params the parameters used to call the action. + * @param signature the signature of the action. + * @return the return value of the action. + * @throws MBeanException if the action throws an exception. The + * thrown exception is the cause of this + * exception. + * @throws ReflectionException if an exception occurred in trying + * to use the reflection interface + * to invoke the action. The + * thrown exception is the cause of + * this exception. + */ + public Object invoke(String name, Object[] params, String[] signature) + throws MBeanException, ReflectionException + { + Class[] sigTypes = new Class[signature.length]; + ClassLoader loader = getClass().getClassLoader(); + for (int a = 0; a < signature.length; ++a) + try + { + sigTypes[a] = Class.forName(signature[a], true, loader); + } + catch (ClassNotFoundException e) + { + throw new ReflectionException(e, "The class, " + signature[a] + + ", in the method signature " + + "could not be loaded."); + } + Method method; + try + { + method = iface.getMethod(name, sigTypes); + } + catch (NoSuchMethodException e) + { + throw new ReflectionException(e, "The method, " + name + + ", could not be found."); + } + Object result; + try + { + result = method.invoke(impl, params); + } + catch (IllegalAccessException e) + { + throw new ReflectionException(e, "Failed to call " + name); + } + catch (IllegalArgumentException e) + { + throw new ReflectionException(e, "Failed to call " + name); + } + catch (InvocationTargetException e) + { + throw new MBeanException((Exception) e.getCause(), "The method " + + name + " threw an exception"); + } + return result; + } + + /** + * Sets the value of the specified attribute of the + * management bean. The management bean should perform + * a lookup for the named attribute, and sets its value + * using the associated setter method, if possible. + * + * @param attribute the attribute to set. + * @throws AttributeNotFoundException if the attribute does not + * correspond to an attribute + * of the bean. + * @throws InvalidAttributeValueException if the value is invalid + * for this particular + * attribute of the bean. + * @throws MBeanException if setting the attribute causes + * the bean to throw an exception (which + * becomes the cause of this exception). + * @throws ReflectionException if an exception occurred in trying + * to use the reflection interface + * to lookup the attribute. The + * thrown exception is the cause of + * this exception. + * @see #getAttribute(String) + */ + public void setAttribute(Attribute attribute) + throws AttributeNotFoundException, InvalidAttributeValueException, + MBeanException, ReflectionException + { + Method setter; + String name = attribute.getName(); + try + { + setter = iface.getMethod("set" + + name.substring(0, 1).toUpperCase() + + name.substring(1), null); + } + catch (NoSuchMethodException e) + { + throw ((AttributeNotFoundException) + new AttributeNotFoundException("The attribute, " + name + + ", was not found.").initCause(e)); + } + try + { + setter.invoke(impl, new Object[] { attribute.getValue() }); + } + catch (IllegalAccessException e) + { + throw new ReflectionException(e, "Failed to set " + name); + } + catch (IllegalArgumentException e) + { + throw ((InvalidAttributeValueException) + new InvalidAttributeValueException(attribute.getValue() + + " is an invalid value for " + + name).initCause(e)); + } + catch (InvocationTargetException e) + { + throw new MBeanException((Exception) e.getCause(), "The getter of " + + name + " threw an exception"); + } + } + + /** + * Sets the value of each of the specified attributes + * to that supplied by the {@link Attribute} object. + * The returned list contains the attributes that were + * set and their new values. + * + * @param attributes the attributes to set. + * @return a list of the changed attributes. + * @see #getAttributes(AttributeList) + */ + public AttributeList setAttributes(AttributeList attributes) + { + AttributeList list = new AttributeList(attributes.size()); + Iterator it = attributes.iterator(); + while (it.hasNext()) + { + try + { + Attribute attrib = (Attribute) it.next(); + setAttribute(attrib); + list.add(attrib); + } + catch (AttributeNotFoundException e) + { + /* Ignored */ + } + catch (InvalidAttributeValueException e) + { + /* Ignored */ + } + catch (ReflectionException e) + { + /* Ignored */ + } + catch (MBeanException e) + { + /* Ignored */ + } + } + return list; + } + + /** + * Replaces the implementation of the interface used by this + * instance with the one specified. The new implementation + * must be non-null and implement the interface specified on + * construction of this instance. + * + * @throws IllegalArgumentException if <code>impl</code> is <code>null</code>. + * @throws NotCompliantMBeanException if <code>impl</code> doesn't implement + * the interface or a method appears + * in the interface that doesn't comply + * with the naming conventions. + */ + public void setImplementation(Object impl) + throws NotCompliantMBeanException + { + if (impl == null) + throw new IllegalArgumentException("The specified implementation is null."); + if (!(iface.isInstance(impl))) + throw new NotCompliantMBeanException("The instance, " + impl + + ", is not an instance of " + iface); + this.impl = impl; + } + +} diff --git a/libjava/classpath/javax/management/openmbean/ArrayType.java b/libjava/classpath/javax/management/openmbean/ArrayType.java new file mode 100644 index 00000000000..d2fc398d623 --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/ArrayType.java @@ -0,0 +1,313 @@ +/* ArrayType.java -- Open type descriptor for an array. + 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 javax.management.openmbean; + +import java.lang.reflect.Array; + +import java.util.Arrays; + +/** + * The open type descriptor for arrays of open data values. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class ArrayType + extends OpenType +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 720504429830309770L; + + /** + * The number of dimensions arrays of this type has. + */ + private int dimension; + + /** + * The element type of arrays of this type. + */ + private OpenType elementType; + + /** + * The hash code of this instance. + */ + private transient Integer hashCode; + + /** + * The <code>toString()</code> result of this instance. + */ + private transient String string; + + /** + * Returns the class name of the array, given the element + * class name and its dimensions. + * + * @param className the name of the class used by the + * array's elements. + * @param dim the dimensions of the array. + * @return the array's class name. + */ + private static String getArrayClassName(String className, int dim) + { + char[] brackets = new char[dim]; + Arrays.fill(brackets, '['); + return String.valueOf(brackets) + "L" + className; + } + + /** + * <p> + * Constructs a new {@link ArrayType} instance for an array of the + * specified type with the supplied number of dimensions. The attributes + * used by the superclass, {@link OpenType}, are automatically defined, + * based on these values. Both the class name and type name are set + * to the value returned by the {@link java.lang.Class#getName()} of + * the array's class (i.e. the element type, preceded by n instances of + * '[' and an 'L', where n is the number of dimensions the array has). + * The description is based upon the template <code>n-dimension array + * of e</code>, where n is the number of dimensions of the array, and + * e is the element type. The class name of the actual elements is + * obtainable by calling {@link OpenType#getClassName()} on the result + * of {@link #getElementOpenType()}. + * </p> + * <p> + * As an example, the array type returned by + * <code>new ArrayType(6, SimpleType.INTEGER)</code> has the following + * values: + * </p> + * <table> + * <th><td>Attribute</td><td>Value</td></th> + * <tr><td>Class Name</td><td><code>[[[[[[Ljava.lang.Integer;</code> + * </td></tr> + * <tr><td>Type Name</td><td><code>[[[[[[Ljava.lang.Integer;</code> + * </td></tr> + * <tr><td>Description</td><td><code>6-dimension array of + * java.lang.Integer</code></td></tr> + * <tr><td>Element Type Class Name</td><td><code>java.lang.Integer</code> + * </td></tr> + * </table> + * <p> + * The dimensions of the array must be equal to or greater than 1. The + * element type must be an instance of {@link SimpleType}, + * {@link CompositeType} or {@link TabularType}. + * </p> + * + * @param dim the dimensions of the array. + * @param elementType the type of the elements of the array. + * @throws IllegalArgumentException if <code>dim</code> is less than 1. + * @throws OpenDataException if the element type is not an instance of either + * {@link SimpleType}, {@link CompositeType} + * or {@link TabularType}. + */ + public ArrayType(int dim, OpenType elementType) + throws OpenDataException + { + super(getArrayClassName(elementType.getClassName(), dim), + getArrayClassName(elementType.getClassName(), dim), + dim + "-dimension array of " + elementType.getClassName()); + if (dim < 1) + throw new IllegalArgumentException("Dimensions must be greater " + + "than or equal to 1."); + if (!(elementType instanceof SimpleType || + elementType instanceof CompositeType || + elementType instanceof TabularType)) + throw new OpenDataException("The element type must be a simple " + + "type, a composite type or a tabular " + + "type."); + dimension = dim; + this.elementType = elementType; + } + + /** + * <p> + * Compares this array type with another object + * for equality. The objects are judged to be equal if: + * </p> + * <ul> + * <li><code>obj</code> is not null.</li> + * <li><code>obj</code> is an instance of + * {@link ArrayType}.</li> + * <li>The dimensions are equal.</li> + * <li>The element types are equal.</li> + * </ul> + * + * @param obj the object to compare with. + * @return true if the conditions above hold. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof ArrayType)) + return false; + ArrayType atype = (ArrayType) obj; + return (atype.getDimension() == dimension && + atype.getElementOpenType().equals(elementType)); + } + + /** + * Returns the number of dimensions used by arrays + * of this type. + * + * @return the number of dimensions. + */ + public int getDimension() + { + return dimension; + } + + /** + * Returns the open type descriptor which describes + * the type of the elements of this array type. + * + * @return the type of the elements. + */ + public OpenType getElementOpenType() + { + return elementType; + } + + /** + * <p> + * Returns the hash code of the array type. + * This is computed as the sum of the hash code of the + * element type together with the number of dimensions + * the array has. These are the same elements + * of the type that are compared as part of the + * {@link #equals(java.lang.Object)} method, thus ensuring + * that the hashcode is compatible with the equality + * test. + * </p> + * <p> + * As instances of this class are immutable, the hash code + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return the hash code of this instance. + */ + public int hashCode() + { + if (hashCode == null) + hashCode = Integer.valueOf(dimension + elementType.hashCode()); + return hashCode.intValue(); + } + + /** + * <p> + * Returns true if the specified object is a member of this + * array type. The object is judged to be so if it is + * non-null, an array and one of the following two conditions + * holds: + * </p> + * <ul> + * <li>This {@link ArrayType} instance has a {@link SimpleType} + * as its element type. Thus, the object must have the same + * class name as that returned by {@link SimpleType#getClassName()} + * for this class.</li> + * <li>This {@link ArrayType} instance has a {@link CompositeType} + * or a {@link TabularType} as its element type. Thus, the object + * must be assignable to such an array, and have elements which + * are either null or valid values for the element type.</li> + * </ul> + * + * @param obj the object to test for membership. + * @return true if the object is a member of this type. + */ + public boolean isValue(Object obj) + { + if (obj == null) + return false; + Class objClass = obj.getClass(); + if (!(objClass.isArray())) + return false; + if (elementType instanceof SimpleType) + return getClassName().equals(objClass.getName()); + Class elementClass = null; + try + { + elementClass = Class.forName(getClassName()); + } + catch (ClassNotFoundException e) + { + throw new IllegalStateException("The array type's element " + + "class could not be found.", e); + } + if (!(elementClass.isAssignableFrom(objClass))) + return false; + for (int a = 0; a < Array.getLength(obj); ++a) + { + Object elem = Array.get(obj, a); + if (elem != null && + (!(elementType.isValue(elem)))) + return false; + } + return true; + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.openmbean.ArrayType</code>) + * and each element of the instance which is relevant to + * the definition of {@link equals(java.lang.Object)} and + * {@link hashCode()} (i.e. the type name, the number of + * dimensions and the element type). + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + string = getClass().getName() + + "[name=" + getTypeName() + + ", dimension=" + dimension + + ", elementType=" + elementType + + "]"; + return string; + } + +} diff --git a/libjava/classpath/javax/management/openmbean/CompositeData.java b/libjava/classpath/javax/management/openmbean/CompositeData.java new file mode 100644 index 00000000000..08f0dc253a5 --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/CompositeData.java @@ -0,0 +1,154 @@ +/* CompositeData.java -- A composite data structure. + 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 javax.management.openmbean; + +import java.util.Collection; + +/** + * Provides an interface to a composite data structure, + * in order to aid interoperability. The composite data + * structure is represented by mapping field names to + * values. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface CompositeData +{ + + /** + * Returns true if this {@link CompositeData} instance contains + * the specified key. This method always returns false for + * an input key equal to <code>null</code> or the empty string. + * + * @param key the key to find in the structure. + * @return true if the key exists. + */ + boolean containsKey(String key); + + /** + * Returns true if this {@link CompositeData} instance has + * a value equal to that supplied. + * + * @param value the value to look for. + * @return true if the value exists. + */ + boolean containsValue(Object value); + + /** + * Compares the specified object with this object for equality. + * The object is judged equivalent if it is non-null, and also + * an instance of {@link CompositeData} with the same name-value + * mappings and types. The two compared instances may be + * equivalent even if they represent different implementations of + * {@link CompositeData}. + * + * @param obj the object to compare for equality. + * @return true if <code>obj</code> is equal to <code>this</code>. + */ + boolean equals(Object obj); + + /** + * Retrieves the value for the specified key. + * + * @param key the key whose value should be returned. + * @return the matching value. + * @throws IllegalArgumentException if the key is <code>null</code> + * or the empty string. + * @throws InvalidKeyException if the key does not exist. + */ + Object get(String key); + + /** + * Returns the appropriate value for each key in the given array, + * using the same ordering. + * + * @param keys the keys whose values should be returned. + * @return the matching values. + * @throws IllegalArgumentException if one of the keys is + * <code>null</code> or the + * empty string. + * @throws InvalidKeyException if one of the keys does not exist. + */ + Object[] getAll(String[] keys); + + /** + * Returns the composite type which corresponds to this instance + * of {@link CompositeData}. + * + * @return the composite type for this instance. + */ + CompositeType getCompositeType(); + + /** + * Returns the hash code of this instance. The hash code is + * computed as the sum of the hash codes of all the values plus + * the hash code of the composite type. As equality comparisons + * take place using this same information, this ensures that + * the property, <code>e1.equals(e2)</code> implies + * <code>e1.hashCode() == e2.hashCode(), holds for any pair + * of instances, <code>e1</code> and <code>e2</code>. + * + * @return the hash code of this {@link CompositeData}. + * @see Object#equals(Object) + */ + int hashCode(); + + /** + * Returns a textual representation of this instance. The + * exact format is left up to the implementation, but it + * should contain the name of the implementing class, + * the name of the type and a mapping of the form + * <code>key=value</code> for each pair of key and value. + * + * @return a {@link java.lang.String} representation of the + * object. + */ + String toString(); + + /** + * Returns a read-only collection of the values associated with + * this instance. The values are sorted using the lexicographic + * ordering of the corresponding keys. + * + * @return the values of this instance. + */ + Collection values(); + +} + diff --git a/libjava/classpath/javax/management/openmbean/CompositeDataSupport.java b/libjava/classpath/javax/management/openmbean/CompositeDataSupport.java new file mode 100644 index 00000000000..5d5adb727f6 --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/CompositeDataSupport.java @@ -0,0 +1,349 @@ +/* CompositeData.java -- A composite data structure implementation. + 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 javax.management.openmbean; + +import java.io.Serializable; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * Provides an implementation of the {@link CompositeData} + * interface. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class CompositeDataSupport + implements CompositeData, Serializable +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 8003518976613702244L; + + /** + * Mapping of field names to values. + * + * @serial the map of field names to values. + */ + private SortedMap contents; + + /** + * The composite type which represents this composite data instance. + * + * @serial the type information for this instance. + */ + private CompositeType compositeType; + + /** + * Constructs a new {@link CompositeDataSupport} instance with the + * specified type using field names and values from the supplied map. + * The keys of the map become the field names, while the values + * become the values of each respective field. This constructor simply + * calls the other constructor, with the two arrays formed using the + * keys and values of this map, respectively. Thus, the input parameters + * given should conform to the same requirements given there (i.e. no + * null values or empty strings). + * + * @param type the composite type of this composite data structure. + * @param items a mapping of field names to values. This should match + * the mappings given by the type (i.e. for each mapping + * in the type, there should be a corresponding field name + * with a value of the correct type). + * @throws IllegalArgumentException if the type, the map or any of the keys + * or values in the map are <code>null</code>, + * or if any key from the map is an empty + * string. + * @throws OpenDataException if a mismatch occurs between the map and the + * field name/type specification given by the + * {@link CompositeType} instance. This may be + * due to the two having a different size, a + * mismatch between keys or an incorrectly typed + * value. + * @throws ArrayStoreException if one of the keys is not a + * {@link java.lang.String} (thus calling a failure + * in converting the keys to an array of strings). + */ + public CompositeDataSupport(CompositeType type, Map items) + throws OpenDataException + { + this(type, + (String[]) items.keySet().toArray(new String[items.size()]), + items.values().toArray()); + } + + /** + * Constructs a new {@link CompositeDataSupport} instance with the + * specified type using the supplied arrays of field names and + * values. Neither the type, the two arrays or any elements of the + * arrays may be <code>null</code>. The {@link java.lang.String}s + * within the <code>names</code> array must be non-empty. The + * arrays must match in size and order, as each element of the + * <code>names</code> array is matched against the corresponding + * value in the <code>values</code> array. Internally, the two are + * stored in a map, lexographically ordered using the field names. + * The data given should also conform to the description of the + * instance given by the {@link CompositeType} instance supplied. + * + * @param type the composite type of this composite data structure. + * @param names the field names. + * @param values the corresponding values of the fields. + * @throws IllegalArgumentException if the type, the arrays or any of the keys + * or values in the arrays are <code>null</code>, + * or if any key from <code>names</code> is + * an empty string. This also occurs if the + * arrays differ in length. + * @throws OpenDataException if a mismatch occurs between the arrays and the + * field name/type specification given by the + * {@link CompositeType} instance. This may be + * due to a differing number of field names, a + * mismatch between names or an incorrectly typed + * value. + */ + public CompositeDataSupport(CompositeType type, String[] names, Object[] values) + throws OpenDataException + { + if (type == null) + throw new IllegalArgumentException("The given composite type is null."); + compositeType = type; + if (names == null) + throw new IllegalArgumentException("The names array is null."); + if (values == null) + throw new IllegalArgumentException("The values array is null."); + if (names.length != values.length) + throw new IllegalArgumentException("The sizes of the arrays differ."); + Set typeKeys = type.keySet(); + if (typeKeys.size() != names.length) + throw new OpenDataException("The number of field names does not match " + + "the type description."); + contents = new TreeMap(); + for (int a = 0; a < names.length; ++a) + { + if (names[a] == null) + throw new IllegalArgumentException("Element " + a + " of the names " + + "array is null."); + if (names[a].length() == 0) + throw new IllegalArgumentException("Element " + a + " of the names " + + "array is an empty string."); + if (values[a] == null) + throw new IllegalArgumentException("Element " + a + " of the values " + + "array is null."); + if (!(typeKeys.contains(names[a]))) + throw new OpenDataException("The name, " + names[a] + ", is not a " + + "field in the given type description."); + if (!(type.getType(names[a]).isValue(values[a]))) + throw new OpenDataException("The value, " + values[a] + ", is not a " + + "valid value for the " + names[a] + " field."); + contents.put(names[a], values[a]); + } + } + + /** + * Returns true if this {@link CompositeData} instance contains + * the specified key. This method always returns false for + * an input key equal to <code>null</code> or the empty string. + * + * @param key the key to find in the structure. + * @return true if the key exists. + */ + public boolean containsKey(String key) + { + if (key == null || key.length() == 0) + return false; + else + return contents.containsKey(key); + } + + /** + * Returns true if this {@link CompositeData} instance has + * a value equal to that supplied. + * + * @param value the value to look for. + * @return true if the value exists. + */ + public boolean containsValue(Object value) + { + return contents.containsValue(value); + } + + + /** + * Compares the specified object with this object for equality. + * The object is judged equivalent if it is non-null, and also + * an instance of {@link CompositeData} with the same name-value + * mappings and types. The two compared instances may be + * equivalent even if they represent different implementations of + * {@link CompositeData}. + * + * @param obj the object to compare for equality. + * @return true if <code>obj</code> is equal to <code>this</code>. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof CompositeData)) + return false; + CompositeData data = (CompositeData) obj; + if (!(data.getCompositeType().equals(compositeType))) + return false; + Iterator it = contents.keySet().iterator(); + while (it.hasNext()) + { + String key = (String) it.next(); + if (!(data.containsKey(key))) + return false; + if (!(data.get(key).equals(contents.get(key)))) + return false; + } + return true; + } + + /** + * Retrieves the value for the specified key. + * + * @param key the key whose value should be returned. + * @return the matching value. + * @throws IllegalArgumentException if the key is <code>null</code> + * or the empty string. + * @throws InvalidKeyException if the key does not exist. + */ + public Object get(String key) + { + if (key == null) + throw new IllegalArgumentException("The supplied key is null."); + if (key.length() == 0) + throw new IllegalArgumentException("The supplied key is the empty string."); + if (!(contents.containsKey(key))) + throw new InvalidKeyException("The supplied key does not exist."); + return contents.get(key); + } + + /** + * Returns the appropriate value for each key in the given array, + * using the same ordering. + * + * @param keys the keys whose values should be returned. + * @return the matching values. + * @throws IllegalArgumentException if one of the keys is + * <code>null</code> or the + * empty string. + * @throws InvalidKeyException if one of the keys does not exist. + */ + public Object[] getAll(String[] keys) + { + Object[] values = new Object[keys.length]; + for (int a = 0; a < keys.length; ++a) + values[a] = get(keys[a]); + return values; + } + + + /** + * Returns the composite type which corresponds to this instance + * of {@link CompositeData}. + * + * @return the composite type for this instance. + */ + public CompositeType getCompositeType() + { + return compositeType; + } + + /** + * Returns the hash code of this instance. The hash code is + * computed as the sum of the hash codes of all the values plus + * the hash code of the composite type. As equality comparisons + * take place using this same information, this should ensure that + * the property, <code>e1.equals(e2)</code> implies + * <code>e1.hashCode() == e2.hashCode(), holds for any pair + * of instances, <code>e1</code> and <code>e2</code>. However, + * this relies on the other instance implementing the + * <code>hashCode</code> method correctly, if it is not an + * instance of {@link CompositeDataSupport}. + * + * @return the hash code of this {@link CompositeData}. + * @see Object#equals(Object) + */ + public int hashCode() + { + int code = compositeType.hashCode(); + Iterator it = values().iterator(); + while (it.hasNext()) + code += it.next().hashCode(); + return code; + } + + + /** + * Returns a textual representation of this instance. The + * exact format is left up to the implementation, but it + * should contain the name of the implementing class, + * the name of the type and a mapping of the form + * <code>key=value</code> for each pair of key and value. + * + * @return a {@link java.lang.String} representation of the + * object. + */ + public String toString() + { + return getClass().getName() + + "[compositeType=" + compositeType + + ",contents=" + contents + + "]"; + } + + /** + * Returns a read-only collection of the values associated with + * this instance. The values are sorted using the lexicographic + * ordering of the corresponding keys. + * + * @return the values of this instance. + */ + public Collection values() + { + return Collections.unmodifiableCollection(contents.values()); + } + +} + diff --git a/libjava/classpath/javax/management/openmbean/CompositeType.java b/libjava/classpath/javax/management/openmbean/CompositeType.java new file mode 100644 index 00000000000..0ae5a4e4bfe --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/CompositeType.java @@ -0,0 +1,324 @@ +/* CompositeType.java -- Type descriptor for CompositeData instances. + 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 javax.management.openmbean; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * The open type descriptor for instances of the + * {@link CompositeData} class. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class CompositeType + extends OpenType +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = -5366242454346948798L; + + /** + * A map of item names to their descriptions. + */ + private TreeMap nameToDescription; + + /** + * A map of item names to their types. + */ + private TreeMap nameToType; + + /** + * The hash code of this instance. + */ + private transient Integer hashCode; + + /** + * The <code>toString()</code> result of this instance. + */ + private transient String string; + + /** + * <p> + * Constructs a new {@link CompositeType} instance for the given + * type name with the specified field names, descriptions and types. + * All parameters, and the elements of the array parameters, must be + * non-null and {@link java.lang.String} values must be something other + * than the empty string. The arrays must be non-empty, and be of + * equal size. + * </p> + * <p> + * The result of <code>CompositeData.class.getName()</code> is adopted + * as the class name (see {@link OpenType}) and changes to the array + * elements following construction of the {@link CompositeType} instance + * will <strong>not</strong> affect the values used by the instance. + * The field names are sorted in to ascending alphanumeric order internally, + * and so ordering can not be used to differentiate between two instances. + * </p> + * + * @param name the name of this composite type. + * @param desc a description of this composite type. + * @param names the names of each field within the composite type. + * @param descs the descriptions of each field within the composite type. + * @param types the types of each field within the composite type. + * @throws IllegalArgumentException if any validity constraint listed above + * is broken. + * @throws OpenDataException if duplicate item names are provided. Item names + * are case-sensitive, but whitespace is removed + * before comparison. + */ + public CompositeType(String name, String desc, String[] names, + String[] descs, OpenType[] types) + throws OpenDataException + { + super(CompositeData.class.getName(), name, desc); + if (names.length == 0 + || names.length != descs.length + || names.length != types.length) + throw new IllegalArgumentException("Arrays must be non-empty " + + "and of equal size."); + nameToDescription = new TreeMap(); + for (int a = 0; a < names.length; ++a) + { + if (names[a] == null) + throw new IllegalArgumentException("Name " + a + " is null."); + if (descs[a] == null) + throw new IllegalArgumentException("Description " + a + + " is null."); + String fieldName = names[a].trim(); + if (fieldName.length() == 0) + throw new IllegalArgumentException("Name " + a + " is " + + "the empty string."); + if (descs[a].length() == 0) + throw new IllegalArgumentException("Description " + a + " is " + + "the empty string."); + if (nameToDescription.containsKey(fieldName)) + throw new OpenDataException(fieldName + " appears more " + + "than once."); + nameToDescription.put(fieldName, descs[a]); + } + nameToType = new TreeMap(); + for (int a = 0; a < names.length; ++a) + nameToType.put(names[a].trim(), types[a]); + } + + /** + * Returns true if this composite data type has a field + * with the given name. + * + * @param name the name of the field to check for. + * @return true if a field of that name exists. + */ + public boolean containsKey(String name) + { + return nameToDescription.containsKey(name); + } + + /** + * <p> + * Compares this composite data type with another object + * for equality. The objects are judged to be equal if: + * </p> + * <ul> + * <li><code>obj</code> is not null.</li> + * <li><code>obj</code> is an instance of + * {@link CompositeType}.</li> + * <li>The type names are equal.</li> + * <li>The fields and their types match.</li> + * </ul> + * + * @param obj the object to compare with. + * @return true if the conditions above hold. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof CompositeType)) + return false; + CompositeType ctype = (CompositeType) obj; + if (!(ctype.getTypeName().equals(getTypeName()))) + return false; + Set keys = keySet(); + if (!(ctype.keySet().equals(keys))) + return false; + Iterator it = keys.iterator(); + while (it.hasNext()) + { + String key = (String) it.next(); + if (!(ctype.getType(key).equals(getType(key)))) + return false; + } + return true; + } + + /** + * Returns the description for the given field name, + * or <code>null</code> if the field name does not + * exist within this composite data type. + * + * @param name the name of the field whose description + * should be returned. + * @return the description, or <code>null</code> if the + * field doesn't exist. + */ + public String getDescription(String name) + { + return (String) nameToDescription.get(name); + } + + /** + * Returns the type for the given field name, + * or <code>null</code> if the field name does not + * exist within this composite data type. + * + * @param name the name of the field whose type + * should be returned. + * @return the type, or <code>null</code> if the + * field doesn't exist. + */ + public OpenType getType(String name) + { + return (OpenType) nameToType.get(name); + } + + /** + * <p> + * Returns the hash code of the composite data type. + * This is computed as the sum of the hash codes of + * each field name and its type, together with the hash + * code of the type name. These are the same elements + * of the type that are compared as part of the + * {@link #equals(java.lang.Object)} method, thus ensuring + * that the hashcode is compatible with the equality + * test. + * </p> + * <p> + * As instances of this class are immutable, the hash code + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return the hash code of this instance. + */ + public int hashCode() + { + if (hashCode == null) + { + int elementTotal = 0; + Iterator it = nameToType.entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry entry = (Map.Entry) it.next(); + elementTotal += (entry.getKey().hashCode() + + entry.getValue().hashCode()); + } + hashCode = Integer.valueOf(elementTotal + + getTypeName().hashCode()); + } + return hashCode.intValue(); + } + + /** + * Returns true if the specified object is a member of this + * composite type. The object is judged to be so if it is + * an instance of {@link CompositeData} with an equivalent + * type, according to the definition of + * {@link #equals(java.lang.Object)} for {@link CompositeType}. + * + * @param obj the object to test for membership. + * @return true if the object is a member of this type. + */ + public boolean isValue(Object obj) + { + if (obj instanceof CompositeData) + { + CompositeData data = (CompositeData) obj; + return equals(data.getCompositeType()); + } + return false; + } + + /** + * Returns an unmodifiable {@link java.util.Set}-based + * view of the field names that form part of this + * {@link CompositeType} instance. The names are stored + * in ascending alphanumeric order. + * + * @return a unmodifiable set containing the field + * name {@link java.lang.String}s. + */ + public Set keySet() + { + return Collections.unmodifiableSet(nameToDescription.keySet()); + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.openmbean.CompositeType</code>) + * and each element of the instance which is relevant to + * the definition of {@link equals(java.lang.Object)} and + * {@link hashCode()} (i.e. the type name, and the name + * and type of each field). + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + string = getClass().getName() + + "[name=" + getTypeName() + + ", fields=" + nameToType + + "]"; + return string; + } + +} diff --git a/libjava/classpath/javax/management/openmbean/InvalidKeyException.java b/libjava/classpath/javax/management/openmbean/InvalidKeyException.java new file mode 100644 index 00000000000..5f0cf76036e --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/InvalidKeyException.java @@ -0,0 +1,77 @@ +/* InvalidKeyException.java -- Thrown by an invalid composite/tabular key. + 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 javax.management.openmbean; + +/** + * Thrown when an invalid key (a field name or row index) is + * passed to a method of the {@link CompositeData} or + * {@link TabularData} classes. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class InvalidKeyException + extends IllegalArgumentException +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 4224269443946322062L; + + /** + * Constructs a new <code>InvalidKeyException</code>. + */ + public InvalidKeyException() + { + super(); + } + + /** + * Constructs a new <code>InvalidKeyException</code> + * with the specified message. + * + * @param message the error message to give to the user. + */ + public InvalidKeyException(String message) + { + super(message); + } + +} + diff --git a/libjava/classpath/javax/management/openmbean/OpenDataException.java b/libjava/classpath/javax/management/openmbean/OpenDataException.java new file mode 100644 index 00000000000..a476cc1d4d2 --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/OpenDataException.java @@ -0,0 +1,79 @@ +/* OpenDataException.java -- Thrown by invalid open bean data. + 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 javax.management.openmbean; + +import javax.management.JMException; + +/** + * Thrown when an instance of one of the open types, open + * data objects or open metadata information objects could + * not be created due to invalid construction parameters. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class OpenDataException + extends JMException +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 8346311255433349870L; + + /** + * Constructs a new <code>OpenDataException</code>. + */ + public OpenDataException() + { + super(); + } + + /** + * Constructs a new <code>OpenDataException</code> + * with the specified message. + * + * @param message the error message to give to the user. + */ + public OpenDataException(String message) + { + super(message); + } + +} + diff --git a/libjava/classpath/javax/management/openmbean/OpenType.java b/libjava/classpath/javax/management/openmbean/OpenType.java new file mode 100644 index 00000000000..b08c40cccce --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/OpenType.java @@ -0,0 +1,230 @@ +/* OpenType.java -- Superclass of all open types. + 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 javax.management.openmbean; + +import java.io.Serializable; + +/** + * The superclass of all open types, which describe the + * applicable data values for open MBeans. An open type + * is defined by its name and description, and the name + * of the Java class it maps to. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public abstract class OpenType + implements Serializable +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = -9195195325186646468L; + + /** + * The name of the Java class this type represents. + */ + private String className; + + /** + * The name of this type. + */ + private String typeName; + + /** + * A description of this type. + */ + private String description; + + /** + * An array which defines the set of Java types which can be + * used as open types. Note that each type is also available + * in array form, possibly with multiple dimensions. + */ + public static final String[] ALLOWED_CLASSNAMES = { + "java.lang.Void", + "java.lang.Boolean", + "java.lang.Character", + "java.lang.Byte", + "java.lang.Short", + "java.lang.Integer", + "java.lang.Long", + "java.lang.Float", + "java.lang.Double", + "java.lang.String", + "java.math.BigDecimal", + "java.math.BigInteger", + "java.util.Date", + "javax.management.ObjectName", + CompositeData.class.getName(), + TabularData.class.getName() + }; + + /** + * Constructs a new {@link OpenType} for the specified class + * with the given name and description. The name of the class + * must be taken from the list of {@link ALLOWED_CLASSNAMES}. + * Arrays are implictly included in this, and follow the usual + * syntax of {@link java.lang.Class#getName()} with the name + * preceded by n instances of '[' (where n is the number of + * dimensions) and an L. The name and description can not be + * <code>null</code> or the empty string. + * + * @param className the name of the Java class this type + * represents. + * @param name the name of the type. + * @param desc the description of the type. + * @throws IllegalArgumentException if either of <code>name</code> + * or <code>desc</code> are + * <code>null</code> or the empty + * string. + * @throws OpenDataException if the class name does not reference + * a listed class (from @{link ALLOWED_CLASSNAMES}) + */ + protected OpenType(String className, String name, String desc) + throws OpenDataException + { + if (name == null || name.equals("")) + throw new IllegalArgumentException("The name can not be null " + + "or the empty string."); + if (desc == null || desc.equals("")) + throw new IllegalArgumentException("The description can not " + + "be null or the empty string."); + String testString; + if (className.startsWith("[")) + testString = className.substring(className.indexOf("L") + 1); + else + testString = className; + boolean openTypeFound = false; + for (int a = 0; a < ALLOWED_CLASSNAMES.length; ++a) + if (ALLOWED_CLASSNAMES[a].equals(className)) + openTypeFound = true; + if (!openTypeFound) + throw new OpenDataException("The class name does not specify " + + "a valid open type."); + this.className = className; + typeName = name; + description = desc; + } + + /** + * Performs an equality test on this object and the one specified. + * + * @param obj the object to test against this one. + * @return true if the two objects are equivalent. + * @see java.lang.Object#hashCode() + */ + public abstract boolean equals(Object obj); + + /** + * Returns the name of the Java class this type represents. This must + * be one of the {@link ALLOWED_CLASSNAMES} or an array of one of them. + * The specification of arrays follows the standard set by + * {@link java.lang.Class#getName()} i.e. the name is the class name + * preceded by n instances of '[' and an 'L', where n is number of + * dimensions used by the array. + * + * @return the class name. + */ + public String getClassName() + { + return className; + } + + /** + * Returns a description of this open type. + * + * @return the description. + */ + public String getDescription() + { + return description; + } + + /** + * Returns the name of this open type. + * + * @return the type name. + */ + public String getTypeName() + { + return typeName; + } + + /** + * Returns a hash code for this open type. The hash code + * should be consistent with the {@link equals()} method. + * Thus, it should continue to return the same value while + * the values used by the {@link equals()} method remain + * the same, and should return different hash codes for + * objects which are judged to be different using the + * {@link equals()} method. + * + * @return the hash code of this instance. + */ + public abstract int hashCode(); + + /** + * Returns true if this open type represents an array type. + * + * @return true if this open type represents an array type. + */ + public boolean isArray() + { + return className.startsWith("["); + } + + /** + * Returns true if the specified object is a member of this + * type. + * + * @param obj the object to test for membership. + * @return true if the object is a member of this type. + */ + public abstract boolean isValue(Object obj); + + /** + * Returns a textual representation of this type. + * + * @return a {@link java.lang.String} representation of this + * type. + */ + public abstract String toString(); + +} diff --git a/libjava/classpath/javax/management/openmbean/SimpleType.java b/libjava/classpath/javax/management/openmbean/SimpleType.java new file mode 100644 index 00000000000..3962909d4bb --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/SimpleType.java @@ -0,0 +1,342 @@ +/* SimpleType.java -- Open type descriptor for the base types. + 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 javax.management.openmbean; + +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; + +/** + * The open type descriptor for data values that are members + * of one of the simple types (such as an integer or a string). + * The other open types ({@link ArrayType}, {@link CompositeType}, + * {@link TabularType}) are constructed from one or more of these + * types. The simple types are formed from a small subset of + * basic Java types. As a result, the valid instances of this + * class are predefined, and no constructor is given for creating + * new instances. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class SimpleType + extends OpenType +{ + + /** + * The {@link SimpleType} representation of + * <code>java.math.BigDecimal</code>. + */ + public static final SimpleType BIGDECIMAL; + + /** + * The {@link SimpleType} representation of + * <code>java.math.BigInteger</code>. + */ + public static final SimpleType BIGINTEGER; + + /** + * The {@link SimpleType} representation of + * <code>java.lang.Boolean</code>. + */ + public static final SimpleType BOOLEAN; + + /** + * The {@link SimpleType} representation of + * <code>java.lang.Byte</code>. + */ + public static final SimpleType BYTE; + + /** + * The {@link SimpleType} representation of + * <code>java.lang.Character</code>. + */ + public static final SimpleType CHARACTER; + + /** + * The {@link SimpleType} representation of + * <code>java.util.Date</code>. + */ + public static final SimpleType DATE; + + /** + * The {@link SimpleType} representation of + * <code>java.lang.Double</code>. + */ + public static final SimpleType DOUBLE; + + /** + * The {@link SimpleType} representation of + * <code>java.lang.Float</code>. + */ + public static final SimpleType FLOAT; + + /** + * The {@link SimpleType} representation of + * <code>java.lang.Integer</code>. + */ + public static final SimpleType INTEGER; + + /** + * The {@link SimpleType} representation of + * <code>java.lang.Long</code>. + */ + public static final SimpleType LONG; + + /** + * The {@link SimpleType} representation of + * <code>javax.management.ObjectName</code>. + */ + public static final SimpleType OBJECTNAME; + + + /** + * The {@link SimpleType} representation of + * <code>java.lang.Short</code>. + */ + public static final SimpleType SHORT; + + /** + * The {@link SimpleType} representation of + * <code>java.lang.String</code>. + */ + public static final SimpleType STRING; + + /** + * The {@link SimpleType} representation of + * <code>java.lang.Void</code>. + */ + public static final SimpleType VOID; + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 2215577471957694503L; + + /** + * The hash code of this instance. + */ + private transient Integer hashCode; + + /** + * The <code>toString()</code> result of this instance. + */ + private transient String string; + + /** + * Static construction of the {@link SimpleType} instances. + */ + static + { + try + { + BIGDECIMAL = new SimpleType("java.math.BigDecimal"); + BIGINTEGER = new SimpleType("java.math.BigInteger"); + BOOLEAN = new SimpleType("java.lang.Boolean"); + BYTE = new SimpleType("java.lang.Byte"); + CHARACTER = new SimpleType("java.lang.Character"); + DATE = new SimpleType("java.util.Date"); + DOUBLE = new SimpleType("java.lang.Double"); + FLOAT = new SimpleType("java.lang.Float"); + INTEGER = new SimpleType("java.lang.Integer"); + LONG = new SimpleType("java.lang.Long"); + OBJECTNAME = new SimpleType("javax.management.ObjectName"); + SHORT = new SimpleType("java.lang.Short"); + STRING = new SimpleType("java.lang.String"); + VOID = new SimpleType("java.lang.Void"); + } + catch (OpenDataException e) + { + /* In normal circumstances, this shouldn't be possible. */ + throw new IllegalStateException("A invalid class name " + + "was passed to the SimpleType " + + "constructor."); + } + } + + /** + * Constructs a new {@link SimpleType} instance for the given + * class name. The class name is also used as the type name + * and description of the {@link OpenType} instance. + * + * @param name the name of the class this instance should + * represent. + * @throws OpenDataException if somehow the constructor of the + * superclass is passed an invalid + * class name. + */ + private SimpleType(String name) + throws OpenDataException + { + super(name, name, name); + } + + /** + * <p> + * Compares this simple data type with another object + * for equality. The objects are judged to be equal if: + * </p> + * <ul> + * <li><code>obj</code> is not null.</li> + * <li><code>obj</code> is an instance of + * {@link SimpleType}.</li> + * <li>The class names are equal.</li> + * </ul> + * + * @param obj the object to compare with. + * @return true if the conditions above hold. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof SimpleType)) + return false; + SimpleType sType = (SimpleType) obj; + return sType.getClassName().equals(getClassName()); + } + + /** + * <p> + * Returns the hash code of the simple data type. + * This is simply the hash code of the class name, + * which is the same element of the type compared + * as part of the + * {@link #equals(java.lang.Object)} method, thus ensuring + * that the hashcode is compatible with the equality + * test. + * </p> + * <p> + * As instances of this class are immutable, the hash code + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return the hash code of this instance. + */ + public int hashCode() + { + if (hashCode == null) + hashCode = Integer.valueOf(getClassName().hashCode()); + return hashCode.intValue(); + } + + /** + * Returns true if the specified object is a member of this + * simple type. The object is judged to be so if it is + * non-null and its class name is the same as that returned + * by {@link SimpleType#getClassName()}. + * + * @param obj the object to test for membership. + * @return true if the object is a member of this type. + */ + public boolean isValue(Object obj) + { + if (obj == null) + return false; + return obj.getClass().getName().equals(getClassName()); + } + + /** + * Replaces instances of this class read from an + * {@link java.io.ObjectInputStream} with one of the predefined + * values. This ensures that each existing instance of + * this class is one of these unique instances. + * + * @return the replacement object. + * @throws ObjectStreamException if the object can not be + * resolved. + */ + public Object readResolve() + throws ObjectStreamException + { + if (equals(BIGDECIMAL)) + return BIGDECIMAL; + if (equals(BIGINTEGER)) + return BIGINTEGER; + if (equals(BOOLEAN)) + return BOOLEAN; + if (equals(BYTE)) + return BYTE; + if (equals(CHARACTER)) + return CHARACTER; + if (equals(DATE)) + return DATE; + if (equals(DOUBLE)) + return DOUBLE; + if (equals(FLOAT)) + return FLOAT; + if (equals(INTEGER)) + return INTEGER; + if (equals(LONG)) + return LONG; + if (equals(OBJECTNAME)) + return OBJECTNAME; + if (equals(SHORT)) + return SHORT; + if (equals(STRING)) + return STRING; + if (equals(VOID)) + return VOID; + throw new InvalidObjectException("Invalid simple type instance " + + "deserialized."); + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.openmbean.SimpleType</code>) + * and the name of the class the type represents. + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + string = getClass().getName() + + "[name=" + getClassName() + + "]"; + return string; + } + +} diff --git a/libjava/classpath/javax/management/openmbean/TabularData.java b/libjava/classpath/javax/management/openmbean/TabularData.java new file mode 100644 index 00000000000..17c8de98109 --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/TabularData.java @@ -0,0 +1,258 @@ +/* TabularData.java -- Tables of composite data structures. + 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 javax.management.openmbean; + +import java.util.Collection; +import java.util.Set; + +/** + * Provides an interface to a specific type of composite + * data structure, where keys (the columns) map to the + * {@link CompositeData} objects that form the rows of + * the table. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface TabularData +{ + + /** + * Calculates the index the specified {@link CompositeData} value + * would have, if it was to be added to this {@link TabularData} + * instance. This method includes a check that the type of + * the given value is the same as the row type of this instance, + * but not a check for existing instances of the given value. + * The value must also not be <code>null</code>. Possible indices + * are returned by the {@link TabularType#getIndexNames()} method + * of this instance's tabular type. + * + * @param val the {@link CompositeData} value whose index should + * be calculated. + * @return the index the value would take on, if it were to be added. + * @throws NullPointerException if the value is <code>null</code>. + * @throws InvalidOpenTypeException if the value does not match the + * row type of this instance. + */ + Object[] calculateIndex(CompositeData val); + + /** + * Removes all {@link CompositeData} values from the table. + */ + void clear(); + + /** + * Returns true iff this instance of the {@link TabularData} class + * contains a {@link CompositeData} value at the specified index. + * In any other circumstance, including if the given key + * is <code>null</code> or of the incorrect type, according to + * the {@link TabularType} of this instance, this method returns + * false. + * + * @param key the key to test for. + * @return true if the key maps to a {@link CompositeData} value. + */ + boolean containsKey(Object[] key); + + /** + * Returns true iff this instance of the {@link TabularData} class + * contains the specified {@link CompositeData} value. + * In any other circumstance, including if the given value + * is <code>null</code> or of the incorrect type, according to + * the {@link TabularType} of this instance, this method returns + * false. + * + * @param val the value to test for. + * @return true if the value exists. + */ + boolean containsValue(CompositeData val); + + /** + * Compares the specified object with this object for equality. + * The object is judged equivalent if it is non-null, and also + * an instance of {@link TabularData} with the same row type, + * and index to value mappings. The two compared instances may + * be equivalent even if they represent different implementations + * of {@link TabularData}. + * + * @param obj the object to compare for equality. + * @return true if <code>obj</code> is equal to <code>this</code>. + */ + boolean equals(Object obj); + + /** + * Retrieves the {@link CompositeData} value for the specified + * key, or <code>null</code> if no such mapping exists. + * + * @param key the key whose value should be returned. + * @return the matching {@link CompositeData} value, or + * <code>null</code> if one does not exist. + * @throws NullPointerException if the key is <code>null</code>. + * @throws InvalidOpenTypeException if the key does not match + * the {@link TabularType} of this + * instance. + */ + CompositeData get(Object[] key); + + /** + * Returns the tabular type which corresponds to this instance + * of {@link TabularData}. + * + * @return the tabular type for this instance. + */ + TabularType getTabularType(); + + /** + * Returns the hash code of the composite data type. + * This is computed as the sum of the hash codes of the + * each index and its value, together with the hash + * code of the tabular type. These are the same elements + * of the type that are compared as part of the + * {@link #equals(java.lang.Object)} method, thus ensuring + * that the hashcode is compatible with the equality + * test. + * + * @return the hash code of this instance. + */ + int hashCode(); + + /** + * Returns true if this {@link TabularData} instance + * contains no {@link CompositeData} values. + * + * @return true if the instance is devoid of rows. + */ + boolean isEmpty(); + + /** + * Returns a {@link java.util.Set} view of the keys or + * indices of this {@link TabularData} instance. + * + * @return a set containing the keys of this instance. + */ + Set keySet(); + + /** + * Adds the specified {@link CompositeData} value to the + * table. The value must be non-null, of the same type + * as the row type of this instance, and must not have + * the same index as an existing value. The index is + * calculated using the index names of the + * {@link TabularType} for this instance. + * + * @param val the {@link CompositeData} value to add. + * @throws NullPointerException if <code>val</code> is + * <code>null</code>. + * @throws InvalidOpenTypeException if the type of the + * given value does not + * match the row type. + * @throws KeyAlreadyExistsException if the value has the + * same calculated index + * as an existing value. + */ + void put(CompositeData val); + + /** + * Adds each of the specified {@link CompositeData} values + * to the table. Each element of the array must meet the + * conditions given for the {@link #put(CompositeData)} + * method. In addition, the index of each value in the + * array must be distinct from the index of the other + * values in the array, as well as from the existing values + * in the table. The operation should be atomic; if one + * value can not be added, then none of the values should + * be. + * + * @param vals the {@link CompositeData} values to add. + * @throws NullPointerException if <code>val</code> is + * <code>null</code>. + * @throws InvalidOpenTypeException if the type of the + * given value does not + * match the row type. + * @throws KeyAlreadyExistsException if the value has the + * same calculated index + * as an existing value or + * of one of the other + * specified values. + */ + void putAll(CompositeData[] vals); + + /** + * Removes the {@link CompositeData} value located at the + * specified index. <code>null</code> is returned if the + * value does not exist. Otherwise, the removed value is + * returned. + * + * @param key the key of the value to remove. + * @return the removed value, or <code>null</code> if + * there is no value for the given key. + * @throws NullPointerException if the key is <code>null</code>. + * @throws InvalidOpenTypeException if the key does not match + * the {@link TabularType} of this + * instance. + */ + CompositeData remove(Object[] key); + + /** + * Returns the number of {@link CompositeData} values or rows + * in the table. + * + * @return the number of rows in the table. + */ + int size(); + + /** + * Returns a textual representation of this instance. The + * exact format is left up to the implementation, but it + * should contain the name of the implementing class and + * the tabular type. + * + * @return a {@link java.lang.String} representation of the + * object. + */ + String toString(); + + /** + * Returns the values associated with this instance. + * + * @return the values of this instance. + */ + Collection values(); + +} + diff --git a/libjava/classpath/javax/management/openmbean/TabularType.java b/libjava/classpath/javax/management/openmbean/TabularType.java new file mode 100644 index 00000000000..c38f5ea626e --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/TabularType.java @@ -0,0 +1,269 @@ +/* TabularType.java -- Type descriptor for TabularData instances. + 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 javax.management.openmbean; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * The open type descriptor for instances of the + * {@link TabularData} class. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class TabularType + extends OpenType +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 6554071860220659261L; + + /** + * The composite type used by the rows of the table. + */ + private CompositeType rowType; + + /** + * The list of index names, which form the columns of the table. + * They are retained in the order given by the user, and is + * unmodifiable. + */ + private List indexNames; + + /** + * The hash code of this instance. + */ + private transient Integer hashCode; + + /** + * The <code>toString()</code> result of this instance. + */ + private transient String string; + + /** + * <p> + * Constructs a new {@link TabularType} instance for the given + * type name, description, row type and index names. All parameters + * (including the individual elements of the array of index names) + * must be non-null and those that are of type {@link java.lang.String} + * must be non-empty. The array of index names must also be non-empty. + * </p> + * <p> + * The result of <code>TabularData.class.getName()</code> is adopted + * as the class name (see {@link OpenType}). The ordering of the array + * elements is relevant in determining the indicies of the values in the + * table, and thus in the use of the + * {@link TabularData#get(java.lang.Object[])} and + * {@link TabularData#remove(java.lang.Object[])} methods of the + * {@link TabularData} class. + * </p> + * + * @param name the name of this tabular type. + * @param desc a description of this tabular type. + * @param rowType the type of the rows of the table. + * @param indexNames the names used to index the rows within the table. + * @throws IllegalArgumentException if any validity constraint listed above + * is broken. + * @throws OpenDataException if an index name does not match a corresponding + * name in the given row type. + */ + public TabularType(String name, String desc, CompositeType rowType, + String[] indexNames) + throws OpenDataException + { + super(TabularData.class.getName(), name, desc); + if (rowType == null) + throw new IllegalArgumentException("A null row type was given."); + for (int a = 0; a < indexNames.length; ++a) + { + if (indexNames[a] == null) + throw new IllegalArgumentException("Name " + a + + " is null."); + if (indexNames[a].length() == 0) + throw new IllegalArgumentException("Name " + a + + " is the empty string."); + if (!(rowType.containsKey(indexNames[a]))) + throw new OpenDataException("No matching key for " + + indexNames[a] + " was found in " + + "the supplied row type."); + } + this.rowType = rowType; + this.indexNames = Collections.unmodifiableList(Arrays.asList(indexNames)); + } + + /** + * <p> + * Compares this tabular data type with another object + * for equality. The objects are judged to be equal if: + * </p> + * <ul> + * <li><code>obj</code> is not null.</li> + * <li><code>obj</code> is an instance of + * {@link TabularType}.</li> + * <li>The type names are equal.</li> + * <li>The row types are equal.</li> + * <li>The index names are equal and in the same order.</li> + * </ul> + * + * @param obj the object to compare with. + * @return true if the conditions above hold. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof TabularType)) + return false; + TabularType ttype = (TabularType) obj; + return (ttype.getTypeName().equals(getTypeName()) + && (ttype.getRowType().equals(getRowType())) + && (ttype.getIndexNames().equals(getIndexNames()))); + } + + /** + * Returns an unmodifiable list containing the index names. + * The ordering of these names is used to determine the indicies + * of the {@link CompositeData} values, and is retained from that + * used in the call to this object's constructor. + * + * @return an unmodifiable list of the index names used by this + * tabular data structure. + */ + public List getIndexNames() + { + return indexNames; + } + + /** + * Returns the type of the rows used by this tabular data structure. + * + * @return the row type. + */ + public CompositeType getRowType() + { + return rowType; + } + + /** + * <p> + * Returns the hash code of the tabular data type. + * This is computed as the sum of the hash codes of the + * index names together with the hash code of the type + * name and row type. These are the same elements + * of the type that are compared as part of the + * {@link #equals(java.lang.Object)} method, thus ensuring + * that the hashcode is compatible with the equality + * test. + * </p> + * <p> + * As instances of this class are immutable, the hash code + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return the hash code of this instance. + */ + public int hashCode() + { + if (hashCode == null) + { + int elementTotal = 0; + Iterator it = indexNames.iterator(); + while (it.hasNext()) + elementTotal += it.next().hashCode(); + hashCode = Integer.valueOf(elementTotal + + getTypeName().hashCode() + + rowType.hashCode()); + } + return hashCode.intValue(); + } + + /** + * Returns true if the specified object is a member of this + * tabular type. The object is judged to be so if it is + * an instance of {@link TabularData} with an equivalent + * type, according to the definition of + * {@link #equals(java.lang.Object)} for {@link TabularType}. + * + * @param obj the object to test for membership. + * @return true if the object is a member of this type. + */ + public boolean isValue(Object obj) + { + if (obj instanceof TabularData) + { + TabularData data = (TabularData) obj; + return equals(data.getTabularType()); + } + return false; + } + + /** + * <p> + * Returns a textual representation of this instance. This + * is constructed using the class name + * (<code>javax.management.openmbean.TabularType</code>) + * and each element of the instance which is relevant to + * the definition of {@link equals(java.lang.Object)} and + * {@link hashCode()} (i.e. the type name, the row type + * and the index names). + * </p> + * <p> + * As instances of this class are immutable, the return value + * is computed just once for each instance and reused + * throughout its life. + * </p> + * + * @return a @link{java.lang.String} instance representing + * the instance in textual form. + */ + public String toString() + { + if (string == null) + string = getClass().getName() + + "[name=" + getTypeName() + + ", rowType=" + rowType + + ", indexNames=" + indexNames + + "]"; + return string; + } + +} diff --git a/libjava/classpath/javax/management/openmbean/package.html b/libjava/classpath/javax/management/openmbean/package.html new file mode 100644 index 00000000000..d915007d4ce --- /dev/null +++ b/libjava/classpath/javax/management/openmbean/package.html @@ -0,0 +1,64 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.lang.management package. + 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. --> + +<html> +<head><title>GNU Classpath - javax.management.openmbean</title></head> + +<body> + +<p> +Provides the data types and descriptor classes for the +<emph>Open MBean</emph>s. Open MBeans are provided in +order to aid interoperability with non-Java management +systems. Unlike normal MBeans, which make use of any +Java data type, Open MBeans use a restricted set of types +which can then be mapped over remote connections, including +to non-Java systems. +</p> +<p> +Normal MBeans are described using an instance of +{@link javax.management.MBeanInfo} with appropriate representations +of the attributes, constructors and operators associated with +the bean. Open MBeans are described in the same way, but by +using subtypes of these entities, which type the bean data using +instances of {@link javax.management.openmbean.OpenType}. Open +types differ from Java types, and are explicitly specified in order +to obtain interoperability with other systems. +</p> +</body> +</html> diff --git a/libjava/classpath/javax/naming/CompositeName.java b/libjava/classpath/javax/naming/CompositeName.java index 6f3466ca23c..322f63279a2 100644 --- a/libjava/classpath/javax/naming/CompositeName.java +++ b/libjava/classpath/javax/naming/CompositeName.java @@ -47,11 +47,18 @@ import java.util.NoSuchElementException; import java.util.Vector; /** + * Represents names that may span over several namespaces. For instance, + * the composite name http://www.gnu.org/software/classpath/index.html spans + * over three namespaces (the protocol http, the web server location + * (www.gnu.org) and the index.html location on the server). + * * @author Tom Tromey (tromey@redhat.com) */ public class CompositeName implements Name, Cloneable, Serializable { private static final long serialVersionUID = 1667768148915813118L; + + private transient Vector elts; public CompositeName () { @@ -331,6 +338,4 @@ public class CompositeName implements Name, Cloneable, Serializable for (int i = 0; i < elts.size(); i++) s.writeObject(elts.get(i)); } - - private transient Vector elts; } diff --git a/libjava/classpath/javax/naming/CompoundName.java b/libjava/classpath/javax/naming/CompoundName.java index b23736fa723..556edfc33f2 100644 --- a/libjava/classpath/javax/naming/CompoundName.java +++ b/libjava/classpath/javax/naming/CompoundName.java @@ -48,6 +48,10 @@ import java.util.Properties; import java.util.Vector; /** + * Represents hierarchical names from the single namespace. For instance, + * the path /home/audriusa/classpath/file.txt is the compound name, using + * the filesystem namespace. + * * @author Tom Tromey (tromey@redhat.com) * @date May 16, 2001 * diff --git a/libjava/classpath/javax/naming/Context.java b/libjava/classpath/javax/naming/Context.java index 9df82c68490..46b540304b4 100644 --- a/libjava/classpath/javax/naming/Context.java +++ b/libjava/classpath/javax/naming/Context.java @@ -1,5 +1,5 @@ -/* Context.java -- - Copyright (C) 2000 Free Software Foundation, Inc. +/* Context.java -- A naming context + Copyright (C) 2000, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,121 +40,453 @@ package javax.naming; import java.util.Hashtable; +import javax.naming.directory.InvalidAttributesException; + public interface Context { - // Property with name of the inital context factory to use - String INITIAL_CONTEXT_FACTORY - = "java.naming.factory.initial"; - - // Property with colon-separated list of object factories to use. - String OBJECT_FACTORIES - = "java.naming.factory.object"; - - // Property with colon-separated list of state factories to use. - String STATE_FACTORIES - = "java.naming.factory.state"; - - // Property with colon-separated list of package prefixes to use. - String URL_PKG_PREFIXES - = "java.naming.factory.url.pkgs"; - - // Property with URL specifying configuration for the service - // provider to use. - String PROVIDER_URL - = "java.naming.provider.url"; + /** + * Property with name of the inital context factory to use + */ + String INITIAL_CONTEXT_FACTORY = "java.naming.factory.initial"; + + /** + * Property with colon-separated list of object factories to use. + */ + String OBJECT_FACTORIES = "java.naming.factory.object"; + + /** + * Property with colon-separated list of state factories to use. + */ + String STATE_FACTORIES = "java.naming.factory.state"; + + /** + * Property with colon-separated list of package prefixes to use. + */ + String URL_PKG_PREFIXES = "java.naming.factory.url.pkgs"; + + /** + * Property with URL specifying configuration for the service provider to use. + */ + String PROVIDER_URL = "java.naming.provider.url"; + + /** + * Property with the DNS host and domain names to use. + */ + String DNS_URL = "java.naming.dns.url"; + + /** + * Property with the authoritativeness of the service requested. + */ + String AUTHORITATIVE = "java.naming.authoritative"; + + /** + * Property with the batch size to use when returning data via the service's + * protocol. + */ + String BATCHSIZE = "java.naming.batchsize"; + + /** + * Property defining how referrals encountered by the service provider are to + * be processed. + */ + String REFERRAL = "java.naming.referral"; + + /** + * Property specifying the security protocol to use. + */ + String SECURITY_PROTOCOL = "java.naming.security.protocol"; + + /** + * Property specifying the security level to use. + */ + String SECURITY_AUTHENTICATION = "java.naming.security.authentication"; + + /** + * Property for the identity of the principal for authenticating the caller to + * the service. + */ + String SECURITY_PRINCIPAL = "java.naming.security.principal"; + + /** + * Property specifying the credentials of the principal for authenticating the + * caller to the service. + */ + String SECURITY_CREDENTIALS = "java.naming.security.credentials"; + + /** + * Property for specifying the preferred language to use with the service. + */ + String LANGUAGE = "java.naming.language"; + + /** + * Property for the initial context constructor to use when searching for + * other properties. + */ + String APPLET = "java.naming.applet"; + + /** + * Give the specified name for the specified object. The passed name must not + * be already bound to some other object. + * + * @param name the name that will be given to the object (in the scope of this + * context). + * @param obj the object being named. + * @throws NameAlreadyBoundException if this name is already used to name some + * object. + * @throws InvalidAttributesException if the object does not supply all + * required attributes. + * @throws NamingException if the naming operation has failed due other + * reasons. + */ + void bind(Name name, Object obj) throws NamingException; + + /** + * Give the specified name for the specified object. The passed name must not + * be already bound to some other object. + * + * @param name the name that will be given to the object (in the scope of this + * context). + * @param obj the object being named. + * @throws NameAlreadyBoundException if this name is already used to name some + * object. + * @throws InvalidAttributesException if the object does not supply all + * required attributes. + * @throws NamingException if the naming operation has failed due other + * reasons. + */ + void bind(String name, Object obj) throws NamingException; + + /** + * Gets the previously named object by name. If the passed name is empty, the + * method should return a cloned instance of this naming context. + * + * @param name the name of the object being searched in this context + * @return the named object + * @throws NamingException if the naming fails. + */ + Object lookup(Name name) throws NamingException; + + /** + * Gets the previously named object by name. If the passed name is empty, the + * method should return a cloned instance of this naming context. + * + * @param name the name of the object being searched in this context + * @return the named object + * @throws NamingException if the naming fails. + */ + Object lookup(String name) throws NamingException; + + /** + * Give the specified name for the specified object. Unlike bind, this method + * silently replaces the existing binding for this name, if one exists. + * + * @param name the name that will be given to the object (in the scope of this + * context). + * @param obj the object being named. + * @throws InvalidAttributesException if the object does not supply all + * required attributes. + * @throws NamingException if the naming operation has failed due other + * reasons. + */ + void rebind(Name name, Object obj) throws NamingException; + + /** + * Give the specified name for the specified object. Unlike bind, this method + * silently replaces the existing binding for this name, if one exists. + * + * @param name the name that will be given to the object (in the scope of this + * context). + * @param obj the object being named. + * @throws InvalidAttributesException if the object does not supply all + * required attributes. + * @throws NamingException if the naming operation has failed due other + * reasons. + */ + void rebind(String name, Object obj) throws NamingException; + + /** + * Removes the name - object mapping from the current context. This method + * returns without action if the name is not bound to an object in the + * terminal context, but throws {@link NameNotFoundException} if one of the + * intermadiate contexts does not exist. + * + * @param name the name to be removed + * @throws NameNotFoundException if one of the intermediate naming contexts + * does not exist. Will not be thrown if just the terminal binding + * is missing. + * @throws NamingException if the naming operation has failed due other + * reasons. + */ + void unbind(Name name) throws NamingException; + + /** + * Removes the name - object mapping from the current context. This method + * returns without action if the name is not bound to an object in the + * terminal context, but throws {@link NameNotFoundException} if one of the + * intermadiate contexts does not exist. + * + * @param name the name to be removed + * @throws NameNotFoundException if one of the intermediate naming contexts + * does not exist. Will not be thrown if just the terminal binding + * is missing. + * @throws NamingException if the naming operation has failed due other + * reasons. + */ + void unbind(String name) throws NamingException; + + /** + * Renames the existing binding, removing the existing and giving the new name + * for the same object. + * + * @param oldName the existing name of the known object + * @param newName the new name of the same object + * @throws NameNotFoundException if the oldName is unknown for this context + * @throws NamingException if the naming operation has failed due other + * reasons. + */ + void rename(Name oldName, Name newName) throws NamingException; + + /** + * Renames the existing binding, removing the existing and giving the new name + * for the same object. + * + * @param oldName the existing name of the known object + * @param newName the new name of the same object + * @throws NameNotFoundException if the oldName is unknown for this context + * @throws NamingException if the naming operation has failed due other + * reasons. + */ + void rename(String oldName, String newName) throws NamingException; + + /** + * Creates and returns the enumeration over the name bindings that are present + * the given subcontext. The enumeration elements have the type of + * {@link NameClassPair}, providing also information about the class of the + * bound object. The behaviour in the case if the bindings are added or + * removed later is not defined. The contents of the subcontexts are not + * included. + * + * @param name the name of the subcontext + * @return the enumeration over the names, known for the given subcontext. + * @throws NamingException + */ + NamingEnumeration list(Name name) throws NamingException; + + /** + * Creates and returns the enumeration over the name bindings that are present + * the given subcontext. The enumeration elements have the type of + * {@link NameClassPair}, providing also information about the class of the + * bound object. The behaviour in the case if the bindings are added or + * removed later is not defined. The contents of the subcontexts are not + * included. + * + * @param name the name of the subcontext + * @return the enumeration over the names, known for the given subcontext. + * @throws NamingException + */ + NamingEnumeration list(String name) throws NamingException; + + /** + * Creates and returns the enumeration over the name - object bindings that + * are present the given subcontext. The enumeration elements have the type of + * {@link Binding}, providing also information about the class of the bound + * object. The behaviour in the case if the bindings are added or removed + * later is not defined. The contents of the subcontexts are not included. + * + * @param name the name of the subcontext + * @return the enumeration over the names, known for the given subcontext. + * @throws NamingException + */ + NamingEnumeration listBindings(Name name) throws NamingException; + + /** + * Creates and returns the enumeration over the name - object bindings that + * are present the given subcontext. The enumeration elements have the type of + * {@link Binding}, providing also information about the class of the bound + * object. The behaviour in the case if the bindings are added or removed + * later is not defined. The contents of the subcontexts are not included. + * + * @param name the name of the subcontext + * @return the enumeration over the names, known for the given subcontext. + * @throws NamingException + */ + NamingEnumeration listBindings(String name) throws NamingException; + + /** + * Creates the new naming subcontext and binds it to the current (this) + * context. + * + * @param name the name of the new context being created + * @return the newly created context, bound to the instance of the context on + * that the method has been called + * @throws NameAlreadyBoundException if this name is already bound + * @throws InvalidAttributesException if the creation of the new context + * requires the missing mandatory attributes + * @throws NamingException + */ + Context createSubcontext(Name name) throws NamingException; + + /** + * Creates the new naming subcontext and binds it to the current (this) + * context. + * + * @param name the name of the new context being created + * @return the newly created context, bound to the instance of the context on + * that the method has been called + * @throws NameAlreadyBoundException if this name is already bound + * @throws InvalidAttributesException if the creation of the new context + * requires the missing mandatory attributes + * @throws NamingException + */ + Context createSubcontext(String name) throws NamingException; + + /** + * Removes the naming subcontext from this naming context. Returns without + * action if such subcontext does not exist. The context being destroyed must + * be empty. + * + * @param name the name of the subcontext beig removed. + * @throws ContextNotEmptyException if the named context is not empty. + * @throws NamingException + */ + void destroySubcontext(Name name) throws NamingException; + + /** + * Removes the naming subcontext from this naming context. Returns without + * action if such subcontext does not exist. The context being destroyed must + * be empty. + * + * @param name the name of the subcontext beig removed. + * @throws ContextNotEmptyException if the named context is not empty. + * @throws NamingException + */ + void destroySubcontext(String name) throws NamingException; + + /** + * Retrieves the named object, not following the link of the terminal atomic + * component of the name. If the object, named by the passed name, is not a + * link, returns that object itself. The intermediate links, if present, are + * followed. + * + * @param name the name of the object that may be a link, leading to another + * object. + * @return the named object, not following the terminal link (if present). + * @throws NamingException + */ + Object lookupLink(Name name) throws NamingException; + + /** + * Retrieves the named object, not following the link of the terminal atomic + * component of the name. If the object, named by the passed name, is not a + * link, returns that object itself. The intermediate links, if present, are + * followed. + * + * @param name the name of the object that may be a link, leading to another + * object. + * @return the named object, not following the terminal link (if present). + * @throws NamingException + */ + Object lookupLink(String name) throws NamingException; - // Property with the DNS host and domain names to use. - String DNS_URL - = "java.naming.dns.url"; + /** + * Obtains the name parser for parsing the names of the given naming + * subcontext. + * + * @param name the name of the subcontext for that the parser must be obtained + * @return the parser to parse the names of that context + * @throws NamingException + */ + NameParser getNameParser(Name name) throws NamingException; + + /** + * Obtains the name parser for parsing the names of the given naming + * subcontext. + * + * @param name the name of the subcontext for that the parser must be obtained + * @return the parser to parse the names of that context + * @throws NamingException + */ + NameParser getNameParser(String name) throws NamingException; - // Property with the authoritativeness of the service requested. - String AUTHORITATIVE - = "java.naming.authoritative"; + /** + * Composes the name of this context together with another name, related to + * this context. + * + * @param name a name, defined in the scope of this context + * @param prefix a name of this context itself, defined in the scope of some + * ancestor + * @return the name of the same object as named by the first parameter, but + * related to the context of the specified ancestor. + * @throws NamingException + */ + Name composeName(Name name, Name prefix) throws NamingException; + + /** + * Composes the name of this context together with another name, related to + * this context. + * + * @param name a name, defined in the scope of this context + * @param prefix a name of this context itself, defined in the scope of some + * ancestor + * @return the name of the same object as named by the first parameter, but + * related to the context of the specified ancestor. + * @throws NamingException + */ + String composeName(String name, String prefix) throws NamingException; - // Property with the batch size to use when returning data via the - // service's protocol. - String BATCHSIZE - = "java.naming.batchsize"; + /** + * Add new environment property to the environment of this context. Both name + * and value of the new property must not be null. If the property is already + * defined, is current value is replaced by the propVal. + * + * @param propName the name of the new property + * @param propVal the value of the new property + * @return the previous value of this property or null if the property has not + * been previously defined + * @throws NamingException + */ + Object addToEnvironment(String propName, Object propVal) + throws NamingException; - // Property defining how referrals encountered by the service - // provider are to be processed. - String REFERRAL - = "java.naming.referral"; - - // Property specifying the security protocol to use. - String SECURITY_PROTOCOL - = "java.naming.security.protocol"; - - // Property specifying the security level to use. - String SECURITY_AUTHENTICATION - = "java.naming.security.authentication"; - - // Property for the identity of the principal for authenticating - // the caller to the service. - String SECURITY_PRINCIPAL - = "java.naming.security.principal"; - - // Property specifying the credentials of the principal for - // authenticating the caller to the service. - String SECURITY_CREDENTIALS - = "java.naming.security.credentials"; - - // Property for specifying the preferred language to use with the - // service. - String LANGUAGE - = "java.naming.language"; - - // Property for the initial context constructor to use when searching - // for other properties. - String APPLET - = "java.naming.applet"; - - void bind (Name name, Object obj) throws NamingException; - void bind (String name, Object obj) throws NamingException; - - Object lookup (Name name) throws NamingException; - Object lookup (String name) throws NamingException; - - void rebind (Name name, Object obj) throws NamingException; - void rebind (String name, Object obj) throws NamingException; - - void unbind (Name name) throws NamingException; - void unbind (String name) throws NamingException; - - void rename (Name oldName, Name newName) throws NamingException; - void rename (String oldName, String newName) throws NamingException; - - NamingEnumeration list (Name name) throws NamingException; - NamingEnumeration list (String name) throws NamingException; - - NamingEnumeration listBindings (Name name) throws NamingException; - NamingEnumeration listBindings (String name) throws NamingException; - - void destroySubcontext (Name name) throws NamingException; - void destroySubcontext (String name) throws NamingException; - - Context createSubcontext (Name name) throws NamingException; - Context createSubcontext (String name) throws NamingException; - - Object lookupLink (Name name) throws NamingException; - Object lookupLink (String name) throws NamingException; - - NameParser getNameParser (Name name) throws NamingException; - NameParser getNameParser (String name) throws NamingException; - - Name composeName (Name name, Name prefix) throws NamingException; - String composeName (String name, - String prefix) throws NamingException; - - Object addToEnvironment (String propName, - Object propVal) throws NamingException; - - Object removeFromEnvironment (String propName) throws NamingException; - - Hashtable getEnvironment () throws NamingException; - - void close () throws NamingException; - - String getNameInNamespace () throws NamingException; + /** + * Removes the property with the given name from the environment. Returns + * without action if this property is not defined. + * + * @param propName the name of the property being removed. + * @return the value of the property that has been removed or null if the + * property was not defined. + * @throws NamingException + */ + Object removeFromEnvironment(String propName) throws NamingException; + + /** + * Returns the environment, associated with this naming context. The returned + * table should never be modified by the caller. Use {@link #addToEnvironment} + * and {@link #removeFromEnvironment} to modify the environement, if needed. + * + * @return the table, representing the environment of this context + * @throws NamingException + */ + Hashtable getEnvironment() throws NamingException; + + /** + * Releases all resources, associated with this context. The close() method + * can be called several times, but after it has been once invoked, it is not + * allowed to call any other method of this context, + * + * @throws NamingException + */ + void close() throws NamingException; + + /** + * Returs the full name of this naming context. The returned string is not a + * JNDI composite name and should not be passed directly to the methods of the + * naming context. + * + * @return the full name of this naming context, in its own namespace. + * @throws OperationNotSupportedException if the naming system, represented by + * this context, does not support the notation of the full name. + * @throws NamingException + */ + String getNameInNamespace() throws NamingException; } - diff --git a/libjava/classpath/javax/naming/ContextNotEmptyException.java b/libjava/classpath/javax/naming/ContextNotEmptyException.java index acbd46bffc4..e856341c1a8 100644 --- a/libjava/classpath/javax/naming/ContextNotEmptyException.java +++ b/libjava/classpath/javax/naming/ContextNotEmptyException.java @@ -38,7 +38,12 @@ exception statement from your version. */ package javax.naming; - +/** + * This exception is thrown in response to the attempt to destroy the non + * empty context. Only empty contexts (without bindings) can be destroyed. + * + * @see Context#destroySubcontext + */ public class ContextNotEmptyException extends NamingException { private static final long serialVersionUID = 1090963683348219877L; diff --git a/libjava/classpath/javax/naming/InitialContext.java b/libjava/classpath/javax/naming/InitialContext.java index 1a9ee5a2738..d4a9587f21c 100644 --- a/libjava/classpath/javax/naming/InitialContext.java +++ b/libjava/classpath/javax/naming/InitialContext.java @@ -1,5 +1,5 @@ -/* InitialContext.java -- - Copyright (C) 2000, 2002, 2003, 2004 Free Software Foundation, Inc. +/* InitialContext.java -- Initial naming context. + Copyright (C) 2000, 2002, 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,170 +43,291 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; +import java.util.HashSet; import java.util.Hashtable; import java.util.Properties; import javax.naming.spi.NamingManager; +/** + * The starting context for performing naming operations. All naming operations + * are performed in the scope of some context. The initial context is the + * starting point for the name resolution. + */ public class InitialContext implements Context { + /** + * Contains the default initial context. This value is returned by + * {@link NamingManager#getInitialContext}. It is set by this method + * when calling it first time. The subsequent calls return the value of + * this field. + */ protected Context defaultInitCtx; + + /** + * Indicates if the initial context was obtained by calling + * {@link NamingManager#getInitialContext}. + */ protected boolean gotDefault = false; + + /** + * The environment, associated with this initial context. + */ protected Hashtable myProps; - public InitialContext (Hashtable environment) - throws NamingException + /** + * The list of the properties, to that the second alternative value must + * be appended after the colon to the first possible value. Used in + * {@link #merge(Hashtable, Hashtable)} + */ + static final HashSet colon_list; + static + { + colon_list = new HashSet(); + colon_list.add(Context.OBJECT_FACTORIES); + colon_list.add(Context.URL_PKG_PREFIXES); + colon_list.add(Context.STATE_FACTORIES); + }; + + /** + * The properties that are searched in the agreed places in the + * {@link #init(Hashtable)} method. + */ + static final String[] use_properties = + { + Context.DNS_URL, + Context.INITIAL_CONTEXT_FACTORY, + Context.OBJECT_FACTORIES, + Context.PROVIDER_URL, + Context.STATE_FACTORIES, + Context.URL_PKG_PREFIXES, + }; + + + /** + * Creates the new initial context with the given properties. + * + * @param environment the properties, used by the initial context being + * created. + * @throws NamingException + */ + public InitialContext(Hashtable environment) throws NamingException { - init (environment); + init(environment); } - protected InitialContext (boolean lazy) - throws NamingException + /** + * Creates the initial context with the possibility to delay its + * initialisation. + * + * @param lazy specified if the initialization should not be performed by this + * constructor (true). If the valueis false, it works the same way as + * the parameterless constructor. + * @throws NamingException + */ + protected InitialContext(boolean lazy) throws NamingException { if (! lazy) - init (null); + init(null); } - public InitialContext () - throws NamingException + /** + * Creates teh new initial context with no properties. Same as + * InitialContext(null). + * + * @throws NamingException + */ + public InitialContext() throws NamingException { - init (null); + init(null); } - /** @since 1.3 */ - protected void init (Hashtable environment) - throws NamingException - { - // FIXME: Is this enough? - final String[] properties = { - Context.DNS_URL, - Context.INITIAL_CONTEXT_FACTORY, - Context.OBJECT_FACTORIES, - Context.PROVIDER_URL, - Context.STATE_FACTORIES, - Context.URL_PKG_PREFIXES, - }; - - // Create myProps, cloning environment if needed. + /** + * <p> + * Initialises the context, using the properties, specified in the passed + * table. + * </p> + * The missing properties are additionally obtained (in order) from the + * following locations: + * <ul> + * <li>If the passed parameter contains the key Context.APPLET, its value + * must be the instance of the {@link Applet}. Then the properties are + * requested via {@link Applet#getParameter(String)}.</li> + * <li>The value of the system property is used.</li> + * <li>The resource "jndi.properties" is requested from the context class + * loader of the current thread</li> + * <li>The property file "jndi.properties" is read from the location, + * specified by the system property "gnu.classpath.home.url". + * </ul> + * </p> + * + * @param environment the table of the properties, may be null. The method + * modifies the table and stores the reference to it. The caller must + * not later reuse this structure for other purposes. + * @since 1.3 + */ + protected void init(Hashtable environment) throws NamingException + { + // If is documented that the caller should not modify the environment. if (environment != null) - myProps = (Hashtable) environment.clone (); + myProps = environment; else - myProps = new Hashtable (); - - Applet napplet = (Applet) myProps.get (Context.APPLET); - - for (int i = properties.length - 1; i >= 0; i--) + myProps = new Hashtable(); + + Applet napplet = (Applet) myProps.get(Context.APPLET); + + Properties pApplet = null; + if (napplet != null) + pApplet = new Properties(); + Properties pSystem = new Properties(); + Object value; + + for (int i = use_properties.length - 1; i >= 0; i--) { - Object o = myProps.get (properties[i]); - - if (o == null) - { - if (napplet != null) - o = napplet.getParameter (properties[i]); - if (o == null) - o = System.getProperty (properties[i]); - if (o != null) - myProps.put (properties[i], o); - } + String key = use_properties[i]; + if (napplet != null) + { + value = napplet.getParameter(key); + if (value != null) + pApplet.put(key, value); + } + + value = System.getProperty(key); + if (value != null) + pSystem.put(key, value); } + + merge(myProps, pSystem); + if (pApplet != null) + merge(myProps, pApplet); try { - Enumeration ep = Thread.currentThread().getContextClassLoader().getResources("jndi.naming"); - while (ep.hasMoreElements ()) - { - URL url = (URL) ep.nextElement (); - Properties p = new Properties (); - - try - { - InputStream is = url.openStream (); - p.load (is); - is.close (); - } - catch (IOException e) - { - } - - merge (myProps, p); - } + Enumeration ep = Thread.currentThread(). + getContextClassLoader().getResources("jndi.properties"); + while (ep.hasMoreElements()) + { + URL url = (URL) ep.nextElement(); + Properties p = new Properties(); + + try + { + InputStream is = url.openStream(); + p.load(is); + is.close(); + } + catch (IOException e) + { + // Ignore. + } + + merge(myProps, p); + } } catch (IOException e) { + // Ignore. } String home = System.getProperty("gnu.classpath.home.url"); if (home != null) { - String url = home + "/jndi.properties"; - Properties p = new Properties (); - - try - { - InputStream is = new URL(url).openStream(); - p.load (is); - is.close (); - } - catch (IOException e) - { - // Ignore. - } - - merge (myProps, p); + String url = home + "/jndi.properties"; + Properties p = new Properties(); + + try + { + InputStream is = new URL(url).openStream(); + p.load(is); + is.close(); + } + catch (IOException e) + { + // Ignore. + } + + merge(myProps, p); } } - - // FIXME: Is this enough? - private static final String[] colon_list = - { - Context.OBJECT_FACTORIES, - Context.URL_PKG_PREFIXES, - Context.STATE_FACTORIES - }; - - private static void merge (Hashtable h1, Hashtable h2) - { - Enumeration e2 = h2.keys(); + + /** + * Merge the content of the two tables. If the second table contains the key + * that is missing in the first table, this key - value pair is copied to the + * first table. If both first and second tables contain the same key AND the + * {@link #colon_list} set also contains this key, the value from the second + * table is appended to the value from the first table after semicolon, and + * the resulted value replaces the value in the first table. + * + * @param primary the first table to merge. The merged result is also stored + * in this table. + * @param additional the second table, from where additional values are taken + */ + static void merge (Hashtable primary, Hashtable additional) + { + Enumeration en = additional.keys(); - while (e2.hasMoreElements()) + while (en.hasMoreElements()) { - String key2 = (String) e2.nextElement(); - Object value1 = h1.get(key2); - if (value1 == null) - h1.put(key2, h2.get(key2)); - else if (key2.compareTo(colon_list[0]) == 0 - || key2.compareTo(colon_list[1]) == 0 - || key2.compareTo(colon_list[2]) == 0 - || key2.compareTo(colon_list[3]) == 0) - { - String value2 = (String) h2.get(key2); - h1.put(key2, (String) value1 + ":" + value2); - } + String key2 = (String) en.nextElement(); + Object value1 = primary.get(key2); + if (value1 == null) + primary.put(key2, additional.get(key2)); + else if (colon_list.contains(key2)) + { + String value2 = (String) additional.get(key2); + primary.put(key2, (String) value1 + ":" + value2); + } } } - - protected Context getDefaultInitCtx () throws NamingException + + /** + * Get the default initial context. If {@link #gotDefault} == false, this + * method obtains the initial context from the naming manager and sets + * gotDefault to true. Otherwise the cached value ({@link #defaultInitCtx} is + * returned. + * + * @return the default initial context + * @throws NamingException + */ + protected Context getDefaultInitCtx() throws NamingException { if (! gotDefault) { - defaultInitCtx = NamingManager.getInitialContext (myProps); - gotDefault = true; + defaultInitCtx = NamingManager.getInitialContext(myProps); + gotDefault = true; } return defaultInitCtx; } - - protected Context getURLOrDefaultInitCtx (Name name) - throws NamingException - { - if (name.size () > 0) - return getURLOrDefaultInitCtx (name.get (0)); + /** + * Obtains the context for resolving the given name. If the first component of + * the name is the URL string, this method tries to find the corressponding + * URL naming context. If it is not an URL string, or the URL context is not + * found, the default initial context is returned. + * + * @param name the name, for that it is required to obtain the context. + * @return the context for resolving the name. + * @throws NamingException + */ + protected Context getURLOrDefaultInitCtx(Name name) throws NamingException + { + if (name.size() > 0) + return getURLOrDefaultInitCtx(name.get(0)); else - return getDefaultInitCtx (); + return getDefaultInitCtx(); } - protected Context getURLOrDefaultInitCtx (String name) - throws NamingException + /** + * Obtains the context for resolving the given name. If the first component of + * the name is the URL string, this method tries to find the corressponding + * URL naming context. If it is not an URL string, or the URL context is not + * found, the default initial context is returned. + * + * @param name the name, for that it is required to obtain the context. + * @return the context for resolving the name. + * @throws NamingException + */ + protected Context getURLOrDefaultInitCtx(String name) throws NamingException { String scheme = null; @@ -214,178 +335,211 @@ public class InitialContext implements Context return getDefaultInitCtx(); int colon = name.indexOf(':'); int slash = name.indexOf('/'); - if (colon > 0 && (slash == -1 || colon < slash)) + if (colon > 0 && (slash == - 1 || colon < slash)) scheme = name.substring(0, colon); - if (scheme != null) + if (scheme != null) { - Context context = - NamingManager.getURLContext(scheme, myProps); - if (context != null) - return context; + Context context = NamingManager.getURLContext(scheme, myProps); + if (context != null) + return context; } - + return getDefaultInitCtx(); } + /** @inheritDoc */ public void bind (Name name, Object obj) throws NamingException { getURLOrDefaultInitCtx (name).bind (name, obj); } + /** @inheritDoc */ public void bind (String name, Object obj) throws NamingException { getURLOrDefaultInitCtx (name).bind (name, obj); } + /** @inheritDoc */ public Object lookup (Name name) throws NamingException { try { - return getURLOrDefaultInitCtx (name).lookup (name); + return getURLOrDefaultInitCtx (name).lookup (name); } catch (CannotProceedException cpe) { - Context ctx = NamingManager.getContinuationContext (cpe); - return ctx.lookup (cpe.getRemainingName()); + Context ctx = NamingManager.getContinuationContext (cpe); + return ctx.lookup (cpe.getRemainingName()); } } + /** @inheritDoc */ public Object lookup (String name) throws NamingException { try - { - return getURLOrDefaultInitCtx (name).lookup (name); - } + { + return getURLOrDefaultInitCtx (name).lookup (name); + } catch (CannotProceedException cpe) - { - Context ctx = NamingManager.getContinuationContext (cpe); - return ctx.lookup (cpe.getRemainingName()); - } + { + Context ctx = NamingManager.getContinuationContext (cpe); + return ctx.lookup (cpe.getRemainingName()); + } } + /** @inheritDoc */ public void rebind (Name name, Object obj) throws NamingException { getURLOrDefaultInitCtx (name).rebind (name, obj); } - + + /** @inheritDoc */ public void rebind (String name, Object obj) throws NamingException { getURLOrDefaultInitCtx (name).rebind (name, obj); } + /** @inheritDoc */ public void unbind (Name name) throws NamingException { getURLOrDefaultInitCtx (name).unbind (name); } + /** @inheritDoc */ public void unbind (String name) throws NamingException { getURLOrDefaultInitCtx (name).unbind (name); } + /** @inheritDoc */ public void rename (Name oldName, Name newName) throws NamingException { getURLOrDefaultInitCtx (oldName).rename (oldName, newName); } + /** @inheritDoc */ public void rename (String oldName, String newName) throws NamingException { getURLOrDefaultInitCtx (oldName).rename (oldName, newName); } + /** @inheritDoc */ public NamingEnumeration list (Name name) throws NamingException { return getURLOrDefaultInitCtx (name).list (name); } + /** @inheritDoc */ public NamingEnumeration list (String name) throws NamingException { return getURLOrDefaultInitCtx (name).list (name); } + /** @inheritDoc */ public NamingEnumeration listBindings (Name name) throws NamingException { return getURLOrDefaultInitCtx (name).listBindings (name); } + /** @inheritDoc */ public NamingEnumeration listBindings (String name) throws NamingException { return getURLOrDefaultInitCtx (name).listBindings (name); } + /** @inheritDoc */ public void destroySubcontext (Name name) throws NamingException { getURLOrDefaultInitCtx (name).destroySubcontext (name); } + /** @inheritDoc */ public void destroySubcontext (String name) throws NamingException { getURLOrDefaultInitCtx (name).destroySubcontext (name); } + /** @inheritDoc */ public Context createSubcontext (Name name) throws NamingException { return getURLOrDefaultInitCtx (name).createSubcontext (name); } + /** @inheritDoc */ public Context createSubcontext (String name) throws NamingException { return getURLOrDefaultInitCtx (name).createSubcontext (name); } + /** @inheritDoc */ public Object lookupLink (Name name) throws NamingException { return getURLOrDefaultInitCtx (name).lookupLink (name); } + /** @inheritDoc */ public Object lookupLink (String name) throws NamingException { return getURLOrDefaultInitCtx (name).lookupLink (name); } + /** @inheritDoc */ public NameParser getNameParser (Name name) throws NamingException { return getURLOrDefaultInitCtx (name).getNameParser (name); } + /** @inheritDoc */ public NameParser getNameParser (String name) throws NamingException { return getURLOrDefaultInitCtx (name).getNameParser (name); } + /** @inheritDoc */ public Name composeName (Name name, Name prefix) throws NamingException { return getURLOrDefaultInitCtx (name).composeName (name, prefix); } + /** @inheritDoc */ public String composeName (String name, - String prefix) throws NamingException + String prefix) throws NamingException { return getURLOrDefaultInitCtx (name).composeName (name, prefix); } - + + /** @inheritDoc */ public Object addToEnvironment (String propName, - Object propVal) throws NamingException + Object propVal) throws NamingException { return myProps.put (propName, propVal); } + /** @inheritDoc */ public Object removeFromEnvironment (String propName) throws NamingException { return myProps.remove (propName); } + /** @inheritDoc */ public Hashtable getEnvironment () throws NamingException { return myProps; } + /** @inheritDoc */ public void close () throws NamingException { myProps = null; defaultInitCtx = null; } + /** + * This operation is not supported for the initial naming context. + * + * @throws OperationNotSupportedException always, unless the method is + * overridden in the derived class. + */ public String getNameInNamespace () throws NamingException { throw new OperationNotSupportedException (); diff --git a/libjava/classpath/javax/naming/NameParser.java b/libjava/classpath/javax/naming/NameParser.java index 1aeaf3600c4..004b406274f 100644 --- a/libjava/classpath/javax/naming/NameParser.java +++ b/libjava/classpath/javax/naming/NameParser.java @@ -1,5 +1,5 @@ -/* NameParser.java -- - Copyright (C) 2000 Free Software Foundation, Inc. +/* NameParser.java -- JNDI name parser interface + Copyright (C) 2000, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,8 +38,24 @@ exception statement from your version. */ package javax.naming; +/** + * Parser the string representation of the given name into the {@link Name} + * representation. + * + * @see Context#getNameParser(String) + * @see Context#getNameParser(Name) + */ public interface NameParser -{ +{ + /** + * Parser the string name representation into the {@link Name} representation + * + * @param name the string representation of the name + * @return the {@link Name} representation of the name. + * @throws InvalidNameException if the name violates the syntax, expected by + * this parser + * @throws NamingException if some other naming exception occurs + */ Name parse (String name) throws NamingException; } diff --git a/libjava/classpath/javax/naming/NamingEnumeration.java b/libjava/classpath/javax/naming/NamingEnumeration.java index 3c9ee2d011d..86b1dfb414d 100644 --- a/libjava/classpath/javax/naming/NamingEnumeration.java +++ b/libjava/classpath/javax/naming/NamingEnumeration.java @@ -1,5 +1,5 @@ -/* NamingEnumeration.java -- - Copyright (C) 2000 Free Software Foundation, Inc. +/* NamingEnumeration.java -- The JNDI enumeration + Copyright (C) 2000, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,9 +40,50 @@ package javax.naming; import java.util.Enumeration; +/** + * <p>The specific type of enumeration that supports throwing various exceptions by + * the hasMore method. The exceptions are only thrown if the enumeration is + * scanned using {@link #next()} and {@link #hasMore()}. If the inherited + * {@link java.util.Enumeration#nextElement()} and + * {@link Enumeration#hasMoreElements()} are used instead, the exceptions are + * not throwed, and the enumeration is just iterated over available elements. + * </p> + * <p>This enumeration becomes invalid after throwing the exception. If the + * exception has been thrown, not other method should be called of that + * enumeration.</p> + */ public interface NamingEnumeration extends Enumeration { - void close() throws NamingException; - boolean hasMore() throws NamingException; + /** + * Returns the next element in this enumeration. The naming - specific + * exceptions are only throws after returning all still available elements of + * the enumeration. + * + * @return the next element of this enumeration + * @throws NamingException + */ Object next() throws NamingException; + + /** + * Checks if there are more unvisited elements in the enumeration, throwing + * exceptions if there are some unvisited, but not available elements. + * + * @return true if there are some unvisited elements, false otherwise. + * @throws PartialResultException if the enumeration, returned by the + * {@link Context#list(Name)} or other similar method contains only + * partial answer. + * @throws SizeLimitExceededException if remaining elements are not available + * because of the previously specified size limit. + * @throws NamingException + */ + boolean hasMore() throws NamingException; + + /** + * Immediately frees all resources, owned by this enumeration. If invoked, it + * must be the last method called for that enumeration. + * + * @throws NamingException + */ + void close() throws NamingException; + } diff --git a/libjava/classpath/javax/naming/PartialResultException.java b/libjava/classpath/javax/naming/PartialResultException.java index 61660781f68..28f0467fe57 100644 --- a/libjava/classpath/javax/naming/PartialResultException.java +++ b/libjava/classpath/javax/naming/PartialResultException.java @@ -38,7 +38,11 @@ exception statement from your version. */ package javax.naming; - +/** + * Thrown from the {@link javax.naming.NamingEnumeration}, this exception + * indicates that the enumeration represents only part of the existing + * elements that would be an answer to the specified request. + */ public class PartialResultException extends NamingException { private static final long serialVersionUID = 2572144970049426786L; diff --git a/libjava/classpath/javax/naming/Reference.java b/libjava/classpath/javax/naming/Reference.java index 5b9883aecd4..0fdecc19ec7 100644 --- a/libjava/classpath/javax/naming/Reference.java +++ b/libjava/classpath/javax/naming/Reference.java @@ -43,54 +43,118 @@ import java.util.Enumeration; import java.util.Vector; /** + * This class represents a reference to an object that is located outside of the + * naming/directory system. + * + * @see Referenceable + * * @author Tom Tromey (tromey@redhat.com) - * @date May 16, 2001 */ public class Reference implements Cloneable, Serializable { private static final long serialVersionUID = - 1673475790065791735L; - + + /** + * The list of addresses, stored in this reference. The object may be + * have by several different addresses. + */ + protected Vector addrs; + + /** + * The name of the class factory to create an instance of the object, + * referenced by this reference. + */ + protected String classFactory; + + /** + * The location, from where the class factory should be loaded. + */ + protected String classFactoryLocation; + + /** + * The name of the class of the object, to that this reference refers. + */ + protected String className; + + /** + * Create a new reference that is referencting to the object of the + * specified class. + */ public Reference (String className) { this.className = className; addrs = new Vector (); } - + + /** + * Create a new reference that is referencing to the object of the + * specified class with the given address. + */ public Reference (String className, RefAddr addr) { this.className = className; addrs = new Vector (); addrs.add (addr); } - - public Reference (String className, String factory, String factoryLocation) + + /** + * Create a new reference that is referencing to the object of the + * specified class, specifying the class and location of the factory that + * produces these objects. + * + * @param className the object class name + * @param factoryClassName the object factory class name + * @param factoryLocation the object factory location + */ + public Reference (String className, String factoryClassName, + String factoryLocation) { this.className = className; - this.classFactory = factory; + this.classFactory = factoryClassName; this.classFactoryLocation = factoryLocation; addrs = new Vector (); } + /** + * Create a new reference that is referencing to the object of the + * specified class, specifying the class and location of the factory that + * produces these objects and also the address of this object. + * + * @param className the object class name + * @param addr the address of the object + * @param factoryClassName the object factory class name + * @param factoryLocation the object factory location + */ public Reference (String className, RefAddr addr, - String factory, String factoryLocation) + String factoryClassName, String factoryLocation) { this.className = className; - this.classFactory = factory; + this.classFactory = factoryClassName; this.classFactoryLocation = factoryLocation; addrs = new Vector (); addrs.add (addr); } + /** + * Add the new address for this object at the given position of the + * address list. + */ public void add (int posn, RefAddr addr) { addrs.add (posn, addr); } - + + /** + * Appends the new object address to the end of the address list. + */ public void add (RefAddr addr) { addrs.add (addr); } - + + /** + * Removes all defined addresses of the object. + */ public void clear () { addrs.clear (); @@ -109,7 +173,10 @@ public class Reference implements Cloneable, Serializable { return (a == null) ? (b == null) : a.equals (b); } - + + /** + * Compares two addresses for equality, by value. + */ public boolean equals (Object obj) { if (! (obj instanceof Reference)) @@ -120,12 +187,23 @@ public class Reference implements Cloneable, Serializable && equals (className, r.className) && addrs.equals (r.addrs)); } - + + /** + * Get the address of this object at the given position. + */ public RefAddr get (int posn) { return (RefAddr) addrs.get (posn); } - + + /** + * Get the given type of address for this object. + * + * @param addrType the needed type of address + * + * @return the address of this object, having the specified type. If there + * is no address of such type, null is returned. + */ public RefAddr get (String addrType) { for (int i = 0; i < addrs.size (); ++i) @@ -136,27 +214,50 @@ public class Reference implements Cloneable, Serializable } return null; } - + + /** + * Get the enumeration over all defined addresses of the object. + */ public Enumeration getAll () { return addrs.elements (); } - + + /** + * Get the name of the class of the referenced object. + * + * @see #className + */ public String getClassName () { return className; } - + + /** + * Get the location of the factory class of the referenced object. + * + * @see #classFactoryLocation + */ public String getFactoryClassLocation () { return classFactoryLocation; } + /** + * Get the name of the factory class of the referenced object + * + * @see #classFactory + */ public String getFactoryClassName () { return classFactory; } - + + /** + * Get the hashcode of this reference. + * + * @return the sum of the hash codes of the addresses. + */ public int hashCode () { // The spec says the hash code is the sum of the hash codes of the @@ -166,17 +267,30 @@ public class Reference implements Cloneable, Serializable h += addrs.get (i).hashCode (); return h; } - + + /** + * Remove the address at the given position. + * + * @param posn the position of the address to remove + * + * @return the removed address + */ public Object remove (int posn) { return addrs.remove (posn); } - + + /** + * Return the number of the defined addresses. + */ public int size () { return addrs.size (); } - + + /** + * Return the string representation. + */ public String toString () { String x = getClass ().toString () + "["; @@ -189,8 +303,4 @@ public class Reference implements Cloneable, Serializable return x + "]"; } - protected Vector addrs; - protected String classFactory; - protected String classFactoryLocation; - protected String className; } diff --git a/libjava/classpath/javax/naming/Referenceable.java b/libjava/classpath/javax/naming/Referenceable.java index cf1100aab83..21c5238bc46 100644 --- a/libjava/classpath/javax/naming/Referenceable.java +++ b/libjava/classpath/javax/naming/Referenceable.java @@ -1,5 +1,5 @@ /* Referenceable.java -- - Copyright (C) 2000 Free Software Foundation, Inc. + Copyright (C) 2000, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,7 +38,19 @@ exception statement from your version. */ package javax.naming; +/** + * The object, implementing this interface, can provided the + * {@link Reference} about itself. + */ public interface Referenceable { + /** + * Get the reference about this object. + * + * @return the reference about this object, cannot be null. + * + * @throws NamingException if the naming exception has been raised while + * retrieving the reference. + */ Reference getReference() throws NamingException; } diff --git a/libjava/classpath/javax/naming/SizeLimitExceededException.java b/libjava/classpath/javax/naming/SizeLimitExceededException.java index 3ca9a23b7b1..caffa70c3e0 100644 --- a/libjava/classpath/javax/naming/SizeLimitExceededException.java +++ b/libjava/classpath/javax/naming/SizeLimitExceededException.java @@ -38,7 +38,12 @@ exception statement from your version. */ package javax.naming; - +/** + * Thrown from the {@link javax.naming.NamingEnumeration}, this exception + * indicates that there are more elements than the previously specified + * size limit. Hence the enumeration represents only part of the existing + * elements that would be an answer to the specified request. + */ public class SizeLimitExceededException extends LimitExceededException { private static final long serialVersionUID = 7129289564879168579L; diff --git a/libjava/classpath/javax/naming/spi/InitialContextFactory.java b/libjava/classpath/javax/naming/spi/InitialContextFactory.java index 7bfcf095e52..d9b3336dcc2 100644 --- a/libjava/classpath/javax/naming/spi/InitialContextFactory.java +++ b/libjava/classpath/javax/naming/spi/InitialContextFactory.java @@ -43,7 +43,28 @@ import java.util.Hashtable; import javax.naming.Context; import javax.naming.NamingException; +/** + * <p> + * Defines a factory that creates the initial context for the beginning of the + * name resolution. JNDI allows to specify different implementations of the + * initial context at runtime. + * </p> + * <p> + * The class, implementing this interface, must be public and have a public + * parameterless constructor + * </p> + */ public interface InitialContextFactory { - Context getInitialContext (Hashtable environment) throws NamingException; + /** + * Create a new initial context + * + * @param environment the properties, used when creating the context. The + * implementing class will not modify the table nor keep the + * reference to it. After the method returns, the caller can safely + * reuse the table for other purposes. + * @return the new initial context + * @throws NamingException if the naming exception has occured + */ + Context getInitialContext(Hashtable environment) throws NamingException; } diff --git a/libjava/classpath/javax/naming/spi/InitialContextFactoryBuilder.java b/libjava/classpath/javax/naming/spi/InitialContextFactoryBuilder.java index 76564ab5ef9..4696cecb8e7 100644 --- a/libjava/classpath/javax/naming/spi/InitialContextFactoryBuilder.java +++ b/libjava/classpath/javax/naming/spi/InitialContextFactoryBuilder.java @@ -42,8 +42,25 @@ import java.util.Hashtable; import javax.naming.NamingException; +/** + * Represents the builder that creates instances of the factories that produce + * initial naming contexts. JNDI allows to specifiy different initial contexts + * at runtime. The user program can install its own initial context factory + * builder. + * + * @see NamingManager#setInitialContextFactoryBuilder + */ public interface InitialContextFactoryBuilder { + /** + * Create the new inital context factory + * + * @param environment the properties, used for creation of the initial context + * factory. The parameter is owned by the caller: it is safe to reuse + * the table for other purposes after the method returns. + * @return the created initial context factory, never null. + * @throws NamingException on failure + */ InitialContextFactory createInitialContextFactory (Hashtable environment) throws NamingException; } diff --git a/libjava/classpath/javax/naming/spi/NamingManager.java b/libjava/classpath/javax/naming/spi/NamingManager.java index cfc9dbd5f10..a36d519d75a 100644 --- a/libjava/classpath/javax/naming/spi/NamingManager.java +++ b/libjava/classpath/javax/naming/spi/NamingManager.java @@ -1,5 +1,6 @@ -/* NamingManager.java -- - Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. +/* NamingManager.java -- Creates contexts and objects + Copyright (C) 2000, 2001, 2002, 2003, 2004, + 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +39,8 @@ exception statement from your version. */ package javax.naming.spi; +import gnu.classpath.VMStackWalker; + import java.util.Enumeration; import java.util.Hashtable; import java.util.StringTokenizer; @@ -52,8 +55,19 @@ import javax.naming.Reference; import javax.naming.Referenceable; import javax.naming.StringRefAddr; +/** + * Contains methods for creating contexts and objects referred to by + * location information. The location is specified in the scope of the + * certain naming or directory service. This class only contais static + * methods and cannot be instantiated. + */ public class NamingManager { + /** + * The environment property into which getContinuationContext() stores the + * value of the CannotProceedException parameter. The value of this field + * is <i>java.naming.spi.CannotProceedException<i>. + */ public static final String CPE = "java.naming.spi.CannotProceedException"; private static InitialContextFactoryBuilder icfb; @@ -65,12 +79,37 @@ public class NamingManager NamingManager () { } - + + /** + * Checks if the initial context factory builder has been set. + * + * @return true if the builder has been set + * + * @see #setInitialContextFactoryBuilder(InitialContextFactoryBuilder) + */ public static boolean hasInitialContextFactoryBuilder () { return icfb != null; } + /** + * Creates the initial context. If the initial object factory builder has + * been set with {@link #setObjectFactoryBuilder(ObjectFactoryBuilder)}, + * the work is delegated to this builder. Otherwise, the method searches + * for the property Context.INITIAL_CONTEXT_FACTORY first in the passed + * table and then in the system properties. The value of this property is + * uses as a class name to install the context factory. The corresponding + * class must exist, be public and have the public parameterless constructor. + * + * @param environment the properties, used to create the context. + * + * @return the created context + * + * @throws NoInitialContextException if the initial builder is not set, + * the property Context.INITIAL_CONTEXT_FACTORY is missing of the + * class, named by this property, cannot be instantiated. + * @throws NamingException if throws by the context factory + */ public static Context getInitialContext (Hashtable environment) throws NamingException { @@ -112,77 +151,191 @@ public class NamingManager return icf.getInitialContext (environment); } - - static Context getURLContext (Object refInfo, - Name name, - Context nameCtx, - String scheme, - Hashtable environment) - throws NamingException + + /** + * <p> + * Creates the URL context for the given URL scheme id. + * </p> + * <p> + * The class name of the factory that creates the context has the naming + * pattern scheme-idURLContextFactory. For instance, the factory for the "ftp" + * sheme should be named "ftpURLContextFactory". + * </p> + * <p> + * The Context.URL_PKG_PREFIXES environment property contains the + * colon-separated list of the possible package prefixes. The package name is + * constructed concatenating the package prefix with the scheme id. This + * property is searched in the passed <i>environment</i> parameter and later + * in the system properties. + * </p> + * <p> + * If the factory class cannot be found in the specified packages, system will + * try to use the default internal factory for the given scheme. + * </p> + * <p> + * After the factory is instantiated, its method + * {@link ObjectFactory#getObjectInstance(Object, Name, Context, Hashtable)} + * is called to create and return the object instance. + * + * @param refInfo passed to the factory + * @param name passed to the factory + * @param nameCtx passed to the factory + * @param scheme the url scheme that must be supported by the given context + * @param environment the properties for creating the factory and context (may + * be null) + * @return the created context + * @throws NamingException if thrown by the factory when creating the context. + */ + static Context getURLContext(Object refInfo, Name name, Context nameCtx, + String scheme, Hashtable environment) + throws NamingException { - String prefixes = null; + // Specified as the default in the docs. Unclear if this is + // right for us. + String defaultPrefix = "com.sun.jndi.url"; + + StringBuffer allPrefixes = new StringBuffer(); + + String prefixes; if (environment != null) - prefixes = (String) environment.get (Context.URL_PKG_PREFIXES); - if (prefixes == null) - prefixes = System.getProperty (Context.URL_PKG_PREFIXES); - if (prefixes == null) { - // Specified as the default in the docs. Unclear if this is - // right for us. - prefixes = "com.sun.jndi.url"; + prefixes = (String) environment.get(Context.URL_PKG_PREFIXES); + if (prefixes != null) + allPrefixes.append(prefixes); + } + + prefixes = System.getProperty(Context.URL_PKG_PREFIXES); + if (prefixes != null) + { + if (allPrefixes.length() > 0) + allPrefixes.append(':'); + allPrefixes.append(prefixes); } + if (allPrefixes.length() > 0) + allPrefixes.append(':'); + allPrefixes.append(defaultPrefix); + scheme = scheme + "." + scheme + "URLContextFactory"; - StringTokenizer tokens = new StringTokenizer (prefixes, ":"); - while (tokens.hasMoreTokens ()) + StringTokenizer tokens = new StringTokenizer(allPrefixes.toString(), ":"); + while (tokens.hasMoreTokens()) { - String aTry = tokens.nextToken (); - try - { - Class factoryClass = Class.forName (aTry + "." + scheme, - true, - Thread.currentThread().getContextClassLoader()); - ObjectFactory factory = - (ObjectFactory) factoryClass.newInstance (); - Object obj = factory.getObjectInstance (refInfo, name, - nameCtx, environment); - Context ctx = (Context) obj; - if (ctx != null) - return ctx; - } - catch (ClassNotFoundException _1) - { - // Ignore it. - } - catch (ClassCastException _2) - { - // This means that the class we found was not an - // ObjectFactory or that the factory returned something - // which was not a Context. - } - catch (InstantiationException _3) - { - // If we couldn't instantiate the factory we might get - // this. - } - catch (IllegalAccessException _4) - { - // Another possibility when instantiating. - } - catch (NamingException _5) - { - throw _5; - } - catch (Exception _6) - { - // Anything from getObjectInstance. - } + String aTry = tokens.nextToken(); + try + { + String tryClass = aTry + "." + scheme; + Class factoryClass = forName(tryClass); + if (factoryClass != null) + { + ObjectFactory factory = (ObjectFactory) factoryClass.newInstance(); + Object obj = factory.getObjectInstance(refInfo, name, nameCtx, + environment); + Context ctx = (Context) obj; + if (ctx != null) + return ctx; + } + } + catch (ClassNotFoundException _1) + { + // Ignore it. + } + catch (ClassCastException _2) + { + // This means that the class we found was not an + // ObjectFactory or that the factory returned something + // which was not a Context. + } + catch (InstantiationException _3) + { + // If we couldn't instantiate the factory we might get + // this. + } + catch (IllegalAccessException _4) + { + // Another possibility when instantiating. + } + catch (NamingException _5) + { + throw _5; + } + catch (Exception _6) + { + // Anything from getObjectInstance. + } } return null; } - + + /** + * Load the class with the given name. This method tries to use the context + * class loader first. If this fails, it searches for the suitable class + * loader in the caller stack trace. This method is a central point where all + * requests to find a class by name are delegated. + */ + static Class forName(String className) + { + try + { + return Class.forName(className, true, + Thread.currentThread().getContextClassLoader()); + } + catch (ClassNotFoundException nex) + { + /** + * Returns the first user defined class loader on the call stack, or + * null when no non-null class loader was found. + */ + Class[] ctx = VMStackWalker.getClassContext(); + for (int i = 0; i < ctx.length; i++) + { + // Since we live in a class loaded by the bootstrap + // class loader, getClassLoader is safe to call without + // needing to be wrapped in a privileged action. + ClassLoader cl = ctx[i].getClassLoader(); + try + { + if (cl != null) + return Class.forName(className, true, cl); + } + catch (ClassNotFoundException nex2) + { + // Try next. + } + } + } + return null; + } + + + /** + * <p> + * Creates the URL context for the given URL scheme id. + * </p> + * <p> + * The class name of the factory that creates the context has the naming + * pattern scheme-idURLContextFactory. For instance, the factory for the "ftp" + * sheme should be named "ftpURLContextFactory". The Context.URL_PKG_PREFIXES + * environment property contains the colon-separated list of the possible + * package prefixes. The package name is constructed concatenating the package + * prefix with the scheme id. + * </p> + * <p> + * If the factory class cannot be found in the specified packages, system will + * try to use the default internal factory for the given scheme. + * </p> + * <p> + * After the factory is instantiated, its method + * {@link ObjectFactory#getObjectInstance(Object, Name, Context, Hashtable)} + * is called to create and return the object instance. + * + * @param scheme the url scheme that must be supported by the given context + * @param environment the properties for creating the factory and context (may + * be null) + * @return the created context + * @throws NamingException if thrown by the factory when creating the context. + */ public static Context getURLContext (String scheme, Hashtable environment) throws NamingException @@ -190,6 +343,17 @@ public class NamingManager return getURLContext (null, null, null, scheme, environment); } + /** + * Sets the initial object factory builder. + * + * @param builder the builder to set + * + * @throws SecurityException if the builder cannot be installed due + * security restrictions. + * @throws NamingException if the builder cannot be installed due other + * reasons + * @throws IllegalStateException if setting the builder repeatedly + */ public static void setObjectFactoryBuilder (ObjectFactoryBuilder builder) throws NamingException { @@ -198,7 +362,7 @@ public class NamingManager sm.checkSetFactory (); // Once the builder is installed it cannot be replaced. if (ofb != null) - throw new IllegalStateException ("builder already installed"); + throw new IllegalStateException ("object factory builder already installed"); if (builder != null) ofb = builder; } @@ -217,7 +381,58 @@ public class NamingManager path += ":" + path2; return new StringTokenizer (path != null ? path : "", ":"); } - + + /** + * <p>Creates an object for the specified name context, environment and + * referencing context object.</p> + * <p> + * If the builder factory is set by + * {@link #setObjectFactoryBuilder(ObjectFactoryBuilder)}, the call is + * delegated to that factory. Otherwise, the object is created using the + * following rules: + * <ul> + * <li>If the referencing object (refInfo) contains the factory class name, + * the object is created by this factory. If the creation fails, + * the parameter refInfo is returned as the method return value.</li> + * <li>If the referencing object has no factory class name, and the addresses + * are StringRefAddrs having the address type "URL", the object is + * created by the URL context factory. The used factory corresponds the + * the naming schema of the each URL. If the attempt to create + * the object this way is not successful, the subsequent rule is + * tried.</li> + * <li> If the refInfo is not an instance of Reference or Referencable + * (for example, null), the object is created by the factories, + * specified in the Context.OBJECT_FACTORIES property of the + * environment and the provider resource file, associated with the + * nameCtx. The value of this property is the colon separated list + * of the possible factories. If none of the factories can be + * loaded, the refInfo is returned. + * </ul> + * </p> + * <p>The object factory must be public and have the public parameterless + * constructor.</p> + * + * @param refInfo the referencing object, for which the new object must be + * created (can be null). If not null, it is usually an instance of + * the {@link Reference} or {@link Referenceable}. + * @param name the name of the object. The name is relative to + * the nameCtx naming context. The value of this parameter can be + * null if the name is not specified. + * @param nameCtx the naming context, in which scope the name of the new + * object is specified. If this parameter is null, the name is + * specified in the scope of the initial context. + * @param environment contains additional information for creating the object. + * This paramter can be null if there is no need to provide any + * additional information. + * + * @return the created object. If the creation fails, in some cases + * the parameter refInfo may be returned. + * + * @throws NamingException if the attempt to name the new object has failed + * @throws Exception if the object factory throws it. The object factory + * only throws an exception if it does not want other factories + * to be used to create the object. + */ public static Object getObjectInstance (Object refInfo, Name name, Context nameCtx, @@ -312,7 +527,21 @@ public class NamingManager return obj == null ? refInfo : obj; } - public static void setInitialContextFactoryBuilder (InitialContextFactoryBuilder builder) + /** + * Sets the initial context factory builder. + * + * @param builder the builder to set + * + * @throws SecurityException if the builder cannot be installed due + * security restrictions. + * @throws NamingException if the builder cannot be installed due other + * reasons + * @throws IllegalStateException if setting the builder repeatedly + * + * @see #hasInitialContextFactoryBuilder() + */ + public static void setInitialContextFactoryBuilder + (InitialContextFactoryBuilder builder) throws NamingException { SecurityManager sm = System.getSecurityManager (); @@ -320,11 +549,23 @@ public class NamingManager sm.checkSetFactory (); // Once the builder is installed it cannot be replaced. if (icfb != null) - throw new IllegalStateException ("builder already installed"); + throw new IllegalStateException ("ctx factory builder already installed"); if (builder != null) icfb = builder; } - + + /** + * Creates a context in which the context operation must be continued. + * This method is used by operations on names that span multiple namespaces. + * + * @param cpe the exception that triggered this continuation. This method + * obtains the environment ({@link CannotProceedException#getEnvironment()} + * and sets the environment property {@link #CPE} = cpe. + * + * @return a non null context for continuing the operation + * + * @throws NamingException if the naming problems have occured + */ public static Context getContinuationContext (CannotProceedException cpe) throws NamingException { @@ -332,7 +573,7 @@ public class NamingManager if (env != null) env.put (CPE, cpe); - // It is really unclear to me if this is right. + // TODO: Check if this implementation matches the API specification try { Object obj = getObjectInstance (cpe.getResolvedObj(), @@ -351,7 +592,22 @@ public class NamingManager throw cpe; } - + + /** + * Get the object state for binding. + * + * @param obj the object, for that the binding state must be retrieved. Cannot + * be null. + * @param name the name of this object, related to the nameCtx. Can be null if + * not specified. + * @param nameCtx the naming context, to that the object name is related. Can + * be null if the name is related to the initial default context. + * @param environment the properties for creating the object state. Can be + * null if no properties are provided. + * @return the object state for binding, may be null if no changes are + * returned by the factory + * @throws NamingException + */ public static Object getStateToBind (Object obj, Name name, Context nameCtx, Hashtable environment) throws NamingException diff --git a/libjava/classpath/javax/naming/spi/ObjectFactory.java b/libjava/classpath/javax/naming/spi/ObjectFactory.java index 81648d9a8b5..27771b6cbbd 100644 --- a/libjava/classpath/javax/naming/spi/ObjectFactory.java +++ b/libjava/classpath/javax/naming/spi/ObjectFactory.java @@ -1,5 +1,5 @@ /* ObjectFactory.java -- - Copyright (C) 2001, 2004 Free Software Foundation, Inc. + Copyright (C) 2001, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,9 +43,33 @@ import java.util.Hashtable; import javax.naming.Context; import javax.naming.Name; +/** + * Represents a factory for creating the object. ObjectFactory performs the + * operation, that is the opposite to the operation, performed by the + * {@link StateFactory}. Classes, implementing this interface, must be public + * and have public parameterless constructor. + */ public interface ObjectFactory { - Object getObjectInstance (Object obj, Name name, Context nameCtx, - Hashtable environment) - throws Exception; + /** + * Creates the object, using the specified name and location information. The + * call of this method must be thread safe. + * + * @param refObj may provide the reference and location information. Can be null. + * @param name the name of the new object in the scope of the specified naming + * context. Can be null if the name is not specified. + * @param nameCtx the context, in which the object name is specified. Can be + * null if the name is specified in the scope of the default initial + * context. + * @param environment the properties, providing additional information on how + * to create an object. Can be null if not additional information is + * provided. + * @return the newly created object or null if the object cannot be created + * @throws Exception if this factory suggest not to try creating of this + * object by other alternative factories + * + * @see NamingManager#getObjectInstance(Object, Name, Context, Hashtable) + */ + Object getObjectInstance(Object refObj, Name name, Context nameCtx, + Hashtable environment) throws Exception; } diff --git a/libjava/classpath/javax/naming/spi/ObjectFactoryBuilder.java b/libjava/classpath/javax/naming/spi/ObjectFactoryBuilder.java index a7613931548..cb20c666e71 100644 --- a/libjava/classpath/javax/naming/spi/ObjectFactoryBuilder.java +++ b/libjava/classpath/javax/naming/spi/ObjectFactoryBuilder.java @@ -1,5 +1,5 @@ -/* ObjectFactoryBuilder.java -- - Copyright (C) 2001, 2004 Free Software Foundation, Inc. +/* ObjectFactoryBuilder.java -- the builder that creates the object factories. + Copyright (C) 2001, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,14 +41,30 @@ package javax.naming.spi; import java.util.Hashtable; import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; /** + * Represents the builder that creates the object factories. + * + * @see NamingManager#setObjectFactoryBuilder(ObjectFactoryBuilder) + * * @author Warren Levy (warrenl@redhat.com) - * @date June 1, 2001 */ public interface ObjectFactoryBuilder -{ - ObjectFactory createObjectFactory(Object obj, +{ + /** + * Create a new object using the supplied environment. + * + * @param refInfo the referencing object, for which the new object must be + * created (can be null). If not null, it is usually an instance of + * the {@link Reference} or {@link Referenceable}. + * @param environment contains the additional information about the factory + * being created. Can be null. + * @return the created object factory. The null is never returned. + * @throws NamingException + */ + ObjectFactory createObjectFactory(Object refInfo, Hashtable environment) throws NamingException; } diff --git a/libjava/classpath/javax/naming/spi/ResolveResult.java b/libjava/classpath/javax/naming/spi/ResolveResult.java index 07e2df3c01c..d5051a5d4da 100644 --- a/libjava/classpath/javax/naming/spi/ResolveResult.java +++ b/libjava/classpath/javax/naming/spi/ResolveResult.java @@ -45,8 +45,11 @@ import javax.naming.InvalidNameException; import javax.naming.Name; /** + * Stores the partial resolution of the name. This class contains the + * object to which part of the name has been resolved and the remaining, + * unresolved part of this name. + * * @author Warren Levy (warrenl@redhat.com) - * @date June 5, 2001 */ public class ResolveResult implements Serializable @@ -54,51 +57,92 @@ public class ResolveResult implements Serializable private static final long serialVersionUID = - 4552108072002407559L; // Serialized fields. + /** + * The object, to that part of the name has been resolved. + */ protected Object resolvedObj; + + /** + * The remaining, unresolved part of the name. + */ protected Name remainingName; - + + /** + * Create the unitialised instance with both parts being null. + */ protected ResolveResult() { - resolvedObj = null; - remainingName = null; } - - public ResolveResult(Object robj, String rcomp) + + /** + * Create the initialised instance + * + * @param resolved the object, to that the name is partially resolved + * @param remaining the remaining unresolved part of the name. + */ + public ResolveResult(Object resolved, String remaining) { - if (robj == null || rcomp == null) + if (resolved == null || remaining == null) throw new IllegalArgumentException (); - resolvedObj = robj; + resolvedObj = resolved; remainingName = new CompositeName (); try { - remainingName.add (rcomp); + remainingName.add (remaining); } catch (InvalidNameException _) { } } - public ResolveResult(Object robj, Name rname) + /** + * Create the initialised instance + * + * @param resolved the object, to that the name is partially resolved + * @param remaining the remaining unresolved part of the name. + */ + public ResolveResult(Object resolved, Name remaining) { - resolvedObj = robj; - remainingName = rname; + resolvedObj = resolved; + remainingName = remaining; } + /** + * Get the remaining unresolved part of the name + * + * @return the remaining unresolved part of the name. + */ public Name getRemainingName() { return remainingName; } + /** + * Get the object to that the name was partially resolved + * + * @return the object, to that the name is partially resolved + */ public Object getResolvedObj() { return resolvedObj; } - + + /** + * Set the remaining unresolved name. + * + * @param name the name being set. The passed parameter is cloned, so the + * caller can reuse or modify it after the method returns. + */ public void setRemainingName(Name name) { remainingName = (Name) name.clone(); } - + + /** + * Append the name to the end of the resolved name. + * + * @param name the name to append + */ public void appendRemainingName(Name name) { try @@ -110,6 +154,11 @@ public class ResolveResult implements Serializable } } + /** + * Append the name to the end of the resolved name. + * + * @param name the name to append + */ public void appendRemainingComponent(String name) { try @@ -121,6 +170,11 @@ public class ResolveResult implements Serializable } } + /** + * Set the object to that the part of the name has been resolved. + * + * @param obj the object, to that the name has been partially resolved. + */ public void setResolvedObj(Object obj) { resolvedObj = obj; diff --git a/libjava/classpath/javax/naming/spi/Resolver.java b/libjava/classpath/javax/naming/spi/Resolver.java index d80fb616924..eb3eeb56079 100644 --- a/libjava/classpath/javax/naming/spi/Resolver.java +++ b/libjava/classpath/javax/naming/spi/Resolver.java @@ -1,5 +1,5 @@ /* Resolver.java -- - Copyright (C) 2001, 2005 Free Software Foundation, Inc. + Copyright (C) 2001, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,18 +38,47 @@ exception statement from your version. */ package javax.naming.spi; +import javax.naming.Context; import javax.naming.Name; import javax.naming.NamingException; +import javax.naming.NotContextException; /** + * <p>Represents the object, capable for the at least partial name resolution. + * The object is not necessay capable for the complete name resolution and + * need not implement the {@link Context}.</p> + * <p> + * Both passed parameters and returned results are owned by the caller.</p> + * * @author Warren Levy (warrenl@redhat.com) - * @date June 1, 2001 */ - public interface Resolver { + /** + * Partially resolve the name, stopping at the first instance of the context + * that is an instance of the contextType + * + * @param name the name to resolve + * @param contextType the class of the context, on that the resolution should + * be terminated + * @return the complete or partial name resolution + * @throws NotContextException if the context of the contextType is not found + * @throws NamingException on other failure + */ ResolveResult resolveToClass(Name name, Class contextType) throws NamingException; + + /** + * Partially resolve the name, stopping at the first instance of the context + * that is an instance of the contextType + * + * @param name the name to resolve + * @param contextType the class of the context, on that the resolution should + * be terminated + * @return the complete or partial name resolution + * @throws NotContextException if the context of the contextType is not found + * @throws NamingException on other failure + */ ResolveResult resolveToClass(String name, Class contextType) throws NamingException; } diff --git a/libjava/classpath/javax/naming/spi/StateFactory.java b/libjava/classpath/javax/naming/spi/StateFactory.java index 1fbdeb1fa1e..5694f8a1a09 100644 --- a/libjava/classpath/javax/naming/spi/StateFactory.java +++ b/libjava/classpath/javax/naming/spi/StateFactory.java @@ -1,5 +1,5 @@ /* StateFactory.java -- - Copyright (C) 2001, 2004 Free Software Foundation, Inc. + Copyright (C) 2001, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -45,11 +45,35 @@ import javax.naming.Name; import javax.naming.NamingException; /** + * Represents a factory, producing the object states for binding. The operation, + * performed by this factory, is the reverse operation with related to the + * operation, performed by the {@link ObjectFactory}. Classes, implementing + * this interface, must be public and have public parameterless constructor. + * + * @see DirStateFactory + * @see ObjectFactory * @author Warren Levy (warrenl@redhat.com) - * @date June 1, 2001 */ public interface StateFactory { + /** + * Get the object state for binding. + * + * @param obj the object, for that the binding state must be retrieved. Cannot + * be null. + * @param name the name of this object, related to the nameCtx. Can be null if + * not specified. + * @param nameCtx the naming context, to that the object name is related. Can + * be null if the name is related to the initial default context. + * @param environment the properties for creating the object state. Can be + * null if no properties are provided. + * @return the object state for binding, may be null if no changes are + * returned by the factory + * @throws NamingException + * + * @see NamingManager#getStateToBind + * @see DirectoryManager#getStateToBind + */ Object getStateToBind(Object obj, Name name, Context nameCtx, Hashtable environment) throws NamingException; } diff --git a/libjava/classpath/javax/rmi/ssl/SslRMIClientSocketFactory.java b/libjava/classpath/javax/rmi/ssl/SslRMIClientSocketFactory.java new file mode 100644 index 00000000000..1fed5824cd4 --- /dev/null +++ b/libjava/classpath/javax/rmi/ssl/SslRMIClientSocketFactory.java @@ -0,0 +1,166 @@ +/* SslRMIClientSocketFactory.java -- + 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 javax.rmi.ssl; + +import java.io.IOException; +import java.io.Serializable; + +import java.util.StringTokenizer; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.SSLSocket; +import java.net.Socket; +import java.rmi.server.RMIClientSocketFactory; + +/** + * SslRMIClientSocketFactory + * + * This class implements an RMIClientSocketFactory for SSL sockets. + * it uses the defeult SSLClientSocketFactory. + * + * This class can optionally use the following system properties, if set: + * <code>javax.rmi.ssl.client.enabledCipherSuites</code> + * <code>javax.rmi.ssl.client.enabledProtocols</code> + * + * These properties will specify a list of SSL/TLS cipher suites and protocols, + * respectively, to enable on the created sockets. + * + * Both properties should consist of a comma-separated list. + * + * @author Sven de Marothy + * @since 1.5 + */ +public class SslRMIClientSocketFactory + implements RMIClientSocketFactory, Serializable +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = -8310631444933958385L; + + private String[] enabledCipherSuites, enabledProtocols; + + /** + * The SSL Socket factory. + */ + private static SSLSocketFactory socketFactory = + (SSLSocketFactory)SSLSocketFactory.getDefault(); + + /** + * Creates a new SslRMIClientSocketFactory + */ + public SslRMIClientSocketFactory() + { + enabledCipherSuites = getProp("javax.rmi.ssl.client.enabledCipherSuites"); + enabledProtocols = getProp("javax.rmi.ssl.client.enabledProtocols"); + } + + private String[] getProp(String p) + { + StringTokenizer st; + try + { + String o = (String)System.getProperty( p ); + st = new StringTokenizer( o, "," ); + } + catch(SecurityException se) + { + return null; + } + + int n = st.countTokens(); + if( n < 1 ) + return null; + String[] strs = new String[ n ]; + for( int i = 0; i < n; i++ ) + strs[i] = st.nextToken().trim(); + + return strs; + } + + /** + * Creates an SSLSocket on a given port + * + * @throws IOException if an error occurs on socket creation. + */ + public Socket createSocket(String host, int port) throws IOException + { + SSLSocket socket = (SSLSocket)socketFactory. + createSocket( host, port ); + if( enabledCipherSuites != null ) + socket.setEnabledCipherSuites( enabledCipherSuites ); + if( enabledProtocols != null ) + socket.setEnabledProtocols( enabledProtocols ); + return socket; + } + + /** + * Compare two SslRMIServerSocketFactor instances + */ + public boolean equals(Object obj) + { + if( !(obj instanceof SslRMIClientSocketFactory) ) + return false; + SslRMIClientSocketFactory s = (SslRMIClientSocketFactory)obj; + + if(!SslRMIServerSocketFactory. + cmpStrArray(enabledCipherSuites, s.enabledCipherSuites)) + return false; + + if(!SslRMIServerSocketFactory. + cmpStrArray(enabledProtocols, s.enabledProtocols)) + return false; + + return true; + } + + /** + * Returns the hash code of this object. + */ + public int hashCode() + { + int hash = 0; + if( enabledCipherSuites != null ) + for(int i = 0; i < enabledCipherSuites.length; i++ ) + hash = hash ^ enabledCipherSuites[i].hashCode(); + if( enabledProtocols != null ) + for(int i = 0; i < enabledProtocols.length; i++ ) + hash = hash ^ enabledProtocols[i].hashCode(); + return hash; + } +} diff --git a/libjava/classpath/javax/rmi/ssl/SslRMIServerSocketFactory.java b/libjava/classpath/javax/rmi/ssl/SslRMIServerSocketFactory.java new file mode 100644 index 00000000000..56f6de11c95 --- /dev/null +++ b/libjava/classpath/javax/rmi/ssl/SslRMIServerSocketFactory.java @@ -0,0 +1,213 @@ +/* SslRMIServerSocketFactory.java -- + 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 javax.rmi.ssl; + +import java.io.IOException; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLServerSocket; +import java.net.ServerSocket; +import java.rmi.server.RMIServerSocketFactory; + +/** + * SslRMIServerSocketFactory + * + * This class implements an RMIServerSocketFactory for SSL sockets. + * it uses the defeult SSLServerSocketFactory. + * + * @author Sven de Marothy + * @since 1.5 + */ +public class SslRMIServerSocketFactory implements RMIServerSocketFactory +{ + private String[] enabledCipherSuites, enabledProtocols; + private boolean needClientAuth; + + /** + * The SSL ServerSocket factory. + */ + private static SSLServerSocketFactory socketFactory = + (SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); + + /** + * Creates a new SslRMIServerSocketFactory with the default socket + * cipher suites and protocols, and without requiring client authorisation. + */ + public SslRMIServerSocketFactory() + { + enabledCipherSuites = enabledProtocols = null; + needClientAuth = false; + } + + /** + * Creates a new SslRMIServerSocketFactory with a given set of socket + * cipher suites and protocols. needClientAuth specifies if client + * authorization is required. + * + * @param enabledCipherSuites - the cypher suites to enable + * or <code>null</code> for the defauls. + * @param enabledCipherSuites - the protocols to enable, + * or <code>null</code> for the defauls. + * @param needClientAuth - specify client authorization requirement. + * @throws IllegalArgumentException if any of the ciphers or protocols + * specified are not available. + */ + public SslRMIServerSocketFactory(String[] enabledCipherSuites, + String[] enabledProtocols, + boolean needClientAuth) + { + this.enabledCipherSuites = enabledCipherSuites; + this.enabledProtocols = enabledProtocols; + this.needClientAuth = needClientAuth; + try + { + if( enabledProtocols != null || enabledCipherSuites != null ) + createServerSocket( 0 ); // stupid way to test the parameters + } + catch(IOException e) + { + // Can this happen? FIXME. + throw new IllegalArgumentException(); + } + } + + /** + * Creates an SSLServerSocket on a given port + * + * @throws IOException if an error occurs on socket creation. + */ + public ServerSocket createServerSocket(int port) throws IOException + { + SSLServerSocket socket = (SSLServerSocket)socketFactory. + createServerSocket( port ); + if( enabledCipherSuites != null ) + socket.setEnabledCipherSuites( enabledCipherSuites ); + if( enabledProtocols != null ) + socket.setEnabledProtocols( enabledProtocols ); + socket.setNeedClientAuth( needClientAuth ); + return socket; + } + + /** + * Compare two SslRMIServerSocketFactor instances + */ + public boolean equals(Object obj) + { + if( !(obj instanceof SslRMIServerSocketFactory) ) + return false; + SslRMIServerSocketFactory s = (SslRMIServerSocketFactory)obj; + if( needClientAuth != s.needClientAuth ) + return false; + + if(!cmpStrArray(enabledCipherSuites, s.enabledCipherSuites)) + return false; + + if(!cmpStrArray(enabledProtocols, s.enabledProtocols)) + return false; + + return true; + } + + /** + * Compare two string arrays. + */ + static boolean cmpStrArray(String[] a, String[] b) + { + if( ( a == null || b == null ) && a != b ) + return false; + + if( a != null ) + { + if( a.length != b.length ) + return false; + for( int i = 0; i < a.length; i++ ) + if(!a[i].equals(b[i])) + return false; + } + + return true; + } + + /** + * Returns the enabled cipher suites, or <code>null</code> + * if the defaults are to be used. + * @returns a string array of cipher suite names + */ + public String[] getEnabledCipherSuites() + { + if( enabledCipherSuites == null ) + return null; + return (String[])enabledCipherSuites.clone(); + } + + /** + * Returns the enabled protocols, or <code>null</code> if the defaults are + * to be used. + * + * @returns a string array of protocol names + */ + public String[] getEnabledProtocols() + { + if( enabledProtocols == null ) + return null; + return (String[])enabledProtocols.clone(); + } + + /** + * Returns whether client authorization is needed. + */ + public boolean getNeedClientAuth() + { + return needClientAuth; + } + + /** + * Returns the hash code of this object. + */ + public int hashCode() + { + int hash = 0; + if( enabledCipherSuites != null ) + for(int i = 0; i < enabledCipherSuites.length; i++ ) + hash = hash ^ enabledCipherSuites[i].hashCode(); + if( enabledProtocols != null ) + for(int i = 0; i < enabledProtocols.length; i++ ) + hash = hash ^ enabledProtocols[i].hashCode(); + hash = ( needClientAuth ) ? (hash^0xFFFF) : hash; + return hash; + } +}
\ No newline at end of file diff --git a/libjava/classpath/javax/security/auth/login/LoginContext.java b/libjava/classpath/javax/security/auth/login/LoginContext.java index 740e67b8baa..17641e65b55 100644 --- a/libjava/classpath/javax/security/auth/login/LoginContext.java +++ b/libjava/classpath/javax/security/auth/login/LoginContext.java @@ -1,5 +1,5 @@ /* LoginContext.java - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -91,6 +91,9 @@ public class LoginContext Configuration config) throws LoginException { + this.name = name; + this.subject = subject; + this.cbHandler = cbHandler; if (config == null) config = Configuration.getConfig(); AppConfigurationEntry[] entries = config.getAppConfigurationEntry (name); @@ -104,9 +107,6 @@ public class LoginContext sharedState = new HashMap(); for (int i = 0; i < entries.length; i++) modules[i] = lookupModule (entries[i], subject, sharedState); - this.name = name; - this.subject = subject; - this.cbHandler = cbHandler; } /** diff --git a/libjava/classpath/javax/sound/midi/SysexMessage.java b/libjava/classpath/javax/sound/midi/SysexMessage.java index 6471a3eb217..3744e2f6bc6 100644 --- a/libjava/classpath/javax/sound/midi/SysexMessage.java +++ b/libjava/classpath/javax/sound/midi/SysexMessage.java @@ -86,7 +86,7 @@ public class SysexMessage extends MidiMessage throws InvalidMidiDataException { if (data[0] != SYSTEM_EXCLUSIVE - || data[0] != SPECIAL_SYSTEM_EXCLUSIVE) + && data[0] != SPECIAL_SYSTEM_EXCLUSIVE) throw new InvalidMidiDataException("Sysex message starts with 0x" + Integer.toHexString(data[0]) + " instead of 0xF0 or 0xF7"); @@ -105,7 +105,7 @@ public class SysexMessage extends MidiMessage throws InvalidMidiDataException { if (status != SYSTEM_EXCLUSIVE - || status != SPECIAL_SYSTEM_EXCLUSIVE) + && status != SPECIAL_SYSTEM_EXCLUSIVE) throw new InvalidMidiDataException("Sysex message starts with 0x" + Integer.toHexString(status) + " instead of 0xF0 or 0xF7"); diff --git a/libjava/classpath/javax/sound/midi/Track.java b/libjava/classpath/javax/sound/midi/Track.java index d06c8cc1dfb..eed5d4c2de0 100644 --- a/libjava/classpath/javax/sound/midi/Track.java +++ b/libjava/classpath/javax/sound/midi/Track.java @@ -54,10 +54,10 @@ public class Track /** * The list of MidiEvents for this track. */ - Vector events; + Vector events = new Vector(); // A HashSet to speed processing - private HashSet eventSet; + private HashSet eventSet = new HashSet(); // This is only instantiable within this package. Track() diff --git a/libjava/classpath/javax/swing/AbstractButton.java b/libjava/classpath/javax/swing/AbstractButton.java index 9b2b526f30b..63f827a1ae0 100644 --- a/libjava/classpath/javax/swing/AbstractButton.java +++ b/libjava/classpath/javax/swing/AbstractButton.java @@ -199,7 +199,7 @@ public abstract class AbstractButton extends JComponent Icon pressed_icon; /** The icon displayed when the button is disabled. */ - Icon disabeldIcon; + Icon disabledIcon; /** The icon displayed when the button is selected. */ Icon selectedIcon; @@ -923,7 +923,7 @@ public abstract class AbstractButton extends JComponent // constructor). // This way the behavior of the JDK is matched. if(text != null) - this.text = text; + setText(text); if (icon != null) default_icon = icon; @@ -1297,9 +1297,11 @@ public abstract class AbstractButton extends JComponent * alignment is a numeric constant from {@link SwingConstants}. It must * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>, * <code>LEADING</code> or <code>TRAILING</code>. The default is - * <code>RIGHT</code>. + * <code>CENTER</code>. * * @return The current horizontal alignment + * + * @see #setHorizontalAlignment(int) */ public int getHorizontalAlignment() { @@ -1311,17 +1313,21 @@ public abstract class AbstractButton extends JComponent * alignment is a numeric constant from {@link SwingConstants}. It must * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>, * <code>LEADING</code> or <code>TRAILING</code>. The default is - * <code>RIGHT</code>. + * <code>CENTER</code>. * * @param a The new horizontal alignment * @throws IllegalArgumentException If alignment is not one of the legal * constants. + * + * @see #getHorizontalAlignment() */ public void setHorizontalAlignment(int a) { if (horizontalAlignment == a) return; - + if (a != LEFT && a != CENTER && a != RIGHT && a != LEADING + && a != TRAILING) + throw new IllegalArgumentException("Invalid alignment."); int old = horizontalAlignment; horizontalAlignment = a; firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a); @@ -1358,6 +1364,9 @@ public abstract class AbstractButton extends JComponent { if (horizontalTextPosition == t) return; + if (t != LEFT && t != CENTER && t != RIGHT && t != LEADING + && t != TRAILING) + throw new IllegalArgumentException("Invalid alignment."); int old = horizontalTextPosition; horizontalTextPosition = t; @@ -1373,6 +1382,8 @@ public abstract class AbstractButton extends JComponent * <code>BOTTOM</code>. The default is <code>CENTER</code>. * * @return The current vertical alignment + * + * @see #setVerticalAlignment(int) */ public int getVerticalAlignment() { @@ -1388,12 +1399,16 @@ public abstract class AbstractButton extends JComponent * @param a The new vertical alignment * @throws IllegalArgumentException If alignment is not one of the legal * constants. + * + * @see #getVerticalAlignment() */ public void setVerticalAlignment(int a) { if (verticalAlignment == a) return; - + if (a != TOP && a != CENTER && a != BOTTOM) + throw new IllegalArgumentException("Invalid alignment."); + int old = verticalAlignment; verticalAlignment = a; firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a); @@ -1430,6 +1445,8 @@ public abstract class AbstractButton extends JComponent { if (verticalTextPosition == t) return; + if (t != TOP && t != CENTER && t != BOTTOM) + throw new IllegalArgumentException("Invalid alignment."); int old = verticalTextPosition; verticalTextPosition = t; @@ -1708,14 +1725,14 @@ public abstract class AbstractButton extends JComponent */ public Icon getDisabledIcon() { - if (disabeldIcon == null && default_icon instanceof ImageIcon) + if (disabledIcon == null && default_icon instanceof ImageIcon) { Image iconImage = ((ImageIcon) default_icon).getImage(); Image grayImage = GrayFilter.createDisabledImage(iconImage); - disabeldIcon = new ImageIcon(grayImage); + disabledIcon = new ImageIcon(grayImage); } - return disabeldIcon; + return disabledIcon; } /** @@ -1729,7 +1746,11 @@ public abstract class AbstractButton extends JComponent */ public void setDisabledIcon(Icon d) { - disabeldIcon = d; + if (disabledIcon == d) + return; + Icon old = disabledIcon; + disabledIcon = d; + firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, old, d); revalidate(); repaint(); } @@ -2092,7 +2113,8 @@ public abstract class AbstractButton extends JComponent } /** - * Set the button's rollover icon. The look and feel class should + * Set the button's rollover icon and sets the <code>rolloverEnabled</code> + * property to <code>true</code>. The look and feel class should * paint this icon when the "rolloverEnabled" property of the button is * <code>true</code> and the mouse rolls over the button. * @@ -2106,6 +2128,7 @@ public abstract class AbstractButton extends JComponent Icon old = rolloverIcon; rolloverIcon = r; firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon); + setRolloverEnabled(true); revalidate(); repaint(); } @@ -2124,12 +2147,13 @@ public abstract class AbstractButton extends JComponent } /** - * Set the button's rollover selected icon. The look and feel class - * should paint this icon when the "rolloverEnabled" property of the button - * is <code>true</code>, the "selected" property of the button's model is - * <code>true</code>, and the mouse rolls over the button. + * Set the button's rollover selected icon and sets the + * <code>rolloverEnabled</code> property to <code>true</code>. The look and + * feel class should paint this icon when the "rolloverEnabled" property of + * the button is <code>true</code>, the "selected" property of the button's + * model is <code>true</code>, and the mouse rolls over the button. * - * @param r The new rollover selected icon + * @param r The new rollover selected icon. */ public void setRolloverSelectedIcon(Icon r) { @@ -2139,6 +2163,7 @@ public abstract class AbstractButton extends JComponent Icon old = rolloverSelectedIcon; rolloverSelectedIcon = r; firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r); + setRolloverEnabled(true); revalidate(); repaint(); } diff --git a/libjava/classpath/javax/swing/AbstractCellEditor.java b/libjava/classpath/javax/swing/AbstractCellEditor.java index df0d3db12b5..e993df5cfc7 100644 --- a/libjava/classpath/javax/swing/AbstractCellEditor.java +++ b/libjava/classpath/javax/swing/AbstractCellEditor.java @@ -134,9 +134,9 @@ public abstract class AbstractCellEditor * * @param listener the CellEditorListener to add */ - public void addCellEditorListener (CellEditorListener listener) + public void addCellEditorListener(CellEditorListener listener) { - listenerList.add (CellEditorListener.class, listener); + listenerList.add(CellEditorListener.class, listener); } /** @@ -145,9 +145,9 @@ public abstract class AbstractCellEditor * * @param listener the CellEditorListener to remove */ - public void removeCellEditorListener (CellEditorListener listener) + public void removeCellEditorListener(CellEditorListener listener) { - listenerList.remove (CellEditorListener.class, listener); + listenerList.remove(CellEditorListener.class, listener); } /** @@ -161,8 +161,8 @@ public abstract class AbstractCellEditor */ public CellEditorListener[] getCellEditorListeners() { - return (CellEditorListener[]) listenerList.getListeners - (CellEditorListener.class); + return (CellEditorListener[]) listenerList.getListeners( + CellEditorListener.class); } /** diff --git a/libjava/classpath/javax/swing/AbstractSpinnerModel.java b/libjava/classpath/javax/swing/AbstractSpinnerModel.java index 1a82f0a359d..089e2048da2 100644 --- a/libjava/classpath/javax/swing/AbstractSpinnerModel.java +++ b/libjava/classpath/javax/swing/AbstractSpinnerModel.java @@ -113,7 +113,7 @@ public abstract class AbstractSpinnerModel implements SpinnerModel { ChangeListener[] listeners = getChangeListeners(); - for(int i = 0; i < listeners.length; ++i) + for (int i = 0; i < listeners.length; ++i) listeners[i].stateChanged(changeEvent); } } diff --git a/libjava/classpath/javax/swing/Box.java b/libjava/classpath/javax/swing/Box.java index 57519f6fcbd..0f984a98465 100644 --- a/libjava/classpath/javax/swing/Box.java +++ b/libjava/classpath/javax/swing/Box.java @@ -196,9 +196,8 @@ public class Box extends JComponent implements Accessible */ public static Component createGlue() { - Filler glue = new Filler(new Dimension(0,0), new Dimension(0,0), - new Dimension(Short.MAX_VALUE,Short.MAX_VALUE) - ); + Filler glue = new Filler(new Dimension(0, 0), new Dimension(0, 0), + new Dimension(Short.MAX_VALUE, Short.MAX_VALUE)); return glue; } @@ -216,9 +215,8 @@ public class Box extends JComponent implements Accessible */ public static Component createHorizontalGlue() { - Filler glue = new Filler(new Dimension(0,0), new Dimension(0,0), - new Dimension(Short.MAX_VALUE, 0) - ); + Filler glue = new Filler(new Dimension(0, 0), new Dimension(0, 0), + new Dimension(Short.MAX_VALUE, 0)); return glue; } diff --git a/libjava/classpath/javax/swing/BoxLayout.java b/libjava/classpath/javax/swing/BoxLayout.java index 408dea934f8..a7f9dd61a6e 100644 --- a/libjava/classpath/javax/swing/BoxLayout.java +++ b/libjava/classpath/javax/swing/BoxLayout.java @@ -426,7 +426,7 @@ public class BoxLayout implements LayoutManager2, Serializable Insets in = container.getInsets(); int width = container.getWidth() - in.left - in.right; - int height = container.getHeight() - in.top -in.bottom; + int height = container.getHeight() - in.top - in.bottom; if (isHorizontalIn(container)) { diff --git a/libjava/classpath/javax/swing/ButtonGroup.java b/libjava/classpath/javax/swing/ButtonGroup.java index 2f8d19831cb..efa36b5f641 100644 --- a/libjava/classpath/javax/swing/ButtonGroup.java +++ b/libjava/classpath/javax/swing/ButtonGroup.java @@ -1,5 +1,5 @@ /* ButtonGroup.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -65,10 +65,9 @@ import java.util.Vector; */ public class ButtonGroup implements Serializable { - /** DOCUMENT ME! */ private static final long serialVersionUID = 4259076101881721375L; - /** The buttons added to this button group. */ + /** Stores references to the buttons added to this button group. */ protected Vector buttons = new Vector(); /** The currently selected button model. */ @@ -83,12 +82,20 @@ public class ButtonGroup implements Serializable } /** - * Adds a button to this group. + * Adds a button to this group. If the button is in the selected state, then: + * <ul> + * <li>if the group has no current selection, the new button becomes the + * selected button for the group;</li> + * <li>if the group already has a selected button, the new button is set to + * "not selected".</li> + * </ul> * - * @param b the button to add + * @param b the button to add (<code>null</code> is ignored). */ public void add(AbstractButton b) { + if (b == null) + return; b.getModel().setGroup(this); if (b.isSelected()) { @@ -96,17 +103,24 @@ public class ButtonGroup implements Serializable sel = b.getModel(); else b.setSelected(false); - } buttons.addElement(b); + } + buttons.addElement(b); } /** - * Removed a given button from this group. + * Removes the specified button from this group. If the button is the + * selected button, the current selection is set to <code>null</code>. + * The group for the removed button's model is set to <code>null</code>. * - * @param b the button to remove + * @param b the button to remove (<code>null</code> is ignored). */ public void remove(AbstractButton b) { + if (b == null) + return; b.getModel().setGroup(null); + if (b.getModel() == sel) + sel = null; buttons.removeElement(b); } @@ -132,19 +146,20 @@ public class ButtonGroup implements Serializable } /** - * DOCUMENT ME! + * Returns the button that has the specified model, or <code>null</code> if + * there is no such button in the group. * - * @param m DOCUMENT ME! + * @param m the button model. * - * @return DOCUMENT ME! + * @return The button that has the specified model, or <code>null</code>. */ - AbstractButton FindButton(ButtonModel m) + AbstractButton findButton(ButtonModel m) { for (int i = 0; i < buttons.size(); i++) { - AbstractButton a = (AbstractButton) buttons.get(i); - if (a.getModel() == m) - return a; + AbstractButton a = (AbstractButton) buttons.get(i); + if (a.getModel() == m) + return a; } return null; } @@ -168,7 +183,7 @@ public class ButtonGroup implements Serializable if (old != null) old.setSelected(false); - AbstractButton button = FindButton(old); + AbstractButton button = findButton(old); if (button != null) button.repaint(); } @@ -180,10 +195,10 @@ public class ButtonGroup implements Serializable * Checks if the given <code>ButtonModel</code> is selected in this button * group. * - * @param m DOCUMENT ME! + * @param m the button model (<code>null</code> permitted). * - * @return true of given <code>ButtonModel</code> is selected, false - * otherwise + * @return <code>true</code> if <code>m</code> is the selected button model + * in this group, and <code>false</code> otherwise. */ public boolean isSelected(ButtonModel m) { diff --git a/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java b/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java index 10de4b94837..efca148f449 100644 --- a/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java +++ b/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java @@ -1,6 +1,6 @@ /* DefaultBoundedRangeModel.java -- Default implementation of BoundedRangeModel. - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,9 @@ exception statement from your version. */ package javax.swing; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.EventListener; @@ -440,4 +443,33 @@ public class DefaultBoundedRangeModel { return (ChangeListener[]) getListeners(ChangeListener.class); } + + /** + * Provides serialization support. + * + * @param stream the output stream (<code>null</code> not permitted). + * + * @throws IOException if there is an I/O error. + */ + private void writeObject(ObjectOutputStream stream) + throws IOException + { + stream.defaultWriteObject(); + } + + /** + * Provides serialization support. + * + * @param stream the input stream (<code>null</code> not permitted). + * + * @throws IOException if there is an I/O error. + * @throws ClassNotFoundException if there is a classpath problem. + */ + private void readObject(ObjectInputStream stream) + throws ClassNotFoundException, IOException + { + stream.defaultReadObject(); + listenerList = new EventListenerList(); + } + } diff --git a/libjava/classpath/javax/swing/DefaultButtonModel.java b/libjava/classpath/javax/swing/DefaultButtonModel.java index 2be18cc8aac..020c904a4e9 100644 --- a/libjava/classpath/javax/swing/DefaultButtonModel.java +++ b/libjava/classpath/javax/swing/DefaultButtonModel.java @@ -466,14 +466,14 @@ public class DefaultButtonModel implements ButtonModel, Serializable if (s) { fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - null, ItemEvent.SELECTED)); + this, ItemEvent.SELECTED)); if (group != null) group.setSelected(this, true); } else { fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - null, ItemEvent.DESELECTED)); + this, ItemEvent.DESELECTED)); if (group != null) group.setSelected(this, false); } diff --git a/libjava/classpath/javax/swing/DefaultComboBoxModel.java b/libjava/classpath/javax/swing/DefaultComboBoxModel.java index ab80b61f1f9..ef785f34dec 100644 --- a/libjava/classpath/javax/swing/DefaultComboBoxModel.java +++ b/libjava/classpath/javax/swing/DefaultComboBoxModel.java @@ -146,9 +146,9 @@ public class DefaultComboBoxModel extends AbstractListModel if (selected == index) // choose a new selected item { if (selected > 0) - selectedItem = getElementAt(selected - 1); + setSelectedItem(getElementAt(selected - 1)); else - selectedItem = getElementAt(selected + 1); + setSelectedItem(getElementAt(selected + 1)); } list.removeElementAt(index); fireIntervalRemoved(this, index, index); diff --git a/libjava/classpath/javax/swing/DefaultListModel.java b/libjava/classpath/javax/swing/DefaultListModel.java index fdbbb562c90..2d02874a7e4 100644 --- a/libjava/classpath/javax/swing/DefaultListModel.java +++ b/libjava/classpath/javax/swing/DefaultListModel.java @@ -184,7 +184,7 @@ public class DefaultListModel extends AbstractListModel /** * Inserts an element at a particular index in the list. Each element at - * index <code>i >= index</code> is shifted to position <code>i+1</code>. + * index <code>i >= index</code> is shifted to position <code>i + 1</code>. * If <code>index</code> is equal to <code>size()</code>, this is * equivalent to appending an element to the array. Any * <code>index</code> greater than <code>size()</code> is illegal. @@ -420,7 +420,7 @@ public class DefaultListModel extends AbstractListModel /** * Inserts an element at a particular index in the list. Each element at - * index <code>i >= index</code> is shifted to position <code>i+1</code>. + * index <code>i >= index</code> is shifted to position <code>i + 1</code>. * If <code>index</code> is equal to <code>size()</code>, this is * equivalent to appending an element to the array. Any * <code>index</code> greater than <code>size()</code> is illegal. diff --git a/libjava/classpath/javax/swing/DefaultListSelectionModel.java b/libjava/classpath/javax/swing/DefaultListSelectionModel.java index 998aee45279..482ce2cc224 100644 --- a/libjava/classpath/javax/swing/DefaultListSelectionModel.java +++ b/libjava/classpath/javax/swing/DefaultListSelectionModel.java @@ -150,9 +150,14 @@ public class DefaultListSelectionModel implements Cloneable, boolean setLeadCalledFromAdd = false; /** - * Gets the value of the {@link #selectionMode} property. - * - * @return The current value of the property + * Returns the selection mode, which is one of {@link #SINGLE_SELECTION}, + * {@link #SINGLE_INTERVAL_SELECTION} and + * {@link #MULTIPLE_INTERVAL_SELECTION}. The default value is + * {@link #MULTIPLE_INTERVAL_SELECTION}. + * + * @return The selection mode. + * + * @see #setSelectionMode(int) */ public int getSelectionMode() { @@ -187,13 +192,19 @@ public class DefaultListSelectionModel implements Cloneable, /** * Sets the value of the {@link #anchorSelectionIndex} property. * - * @param anchorIndex The new property value + * @param index The new property value * * @see #getAnchorSelectionIndex */ - public void setAnchorSelectionIndex(int anchorIndex) + public void setAnchorSelectionIndex(int index) { - anchorSelectionIndex = anchorIndex; + if (anchorSelectionIndex != index) + { + int old = anchorSelectionIndex; + anchorSelectionIndex = index; + if (leadAnchorNotificationEnabled) + fireValueChanged(index, old); + } } /** @@ -459,12 +470,14 @@ public class DefaultListSelectionModel implements Cloneable, if (index0 == -1 || index1 == -1) return; + if (selectionMode == SINGLE_SELECTION) + setSelectionInterval(index0, index1); + else + { int lo = Math.min(index0, index1); int hi = Math.max(index0, index1); oldSel = sel.clone(); - if (selectionMode == SINGLE_SELECTION) - setSelectionInterval(index0, index1); // COMPAT: Like Sun (but not like IBM), we allow calls to // addSelectionInterval when selectionMode is @@ -503,6 +516,7 @@ public class DefaultListSelectionModel implements Cloneable, sel.set(lo, hi+1); fireDifference(sel, (BitSet) oldSel); } + } } @@ -588,27 +602,82 @@ public class DefaultListSelectionModel implements Cloneable, * the current selection mode is <code>SINGLE_SELECTION</code> only the * index <code>index2</code> is selected. * - * @param index0 The low end of the new selection - * @param index1 The high end of the new selection + * @param anchor the anchor selection index. + * @param lead the lead selection index. */ - public void setSelectionInterval(int index0, int index1) + public void setSelectionInterval(int anchor, int lead) { - if (index0 == -1 || index1 == -1) + if (anchor == -1 || lead == -1) return; - - BitSet oldSel = (BitSet) sel.clone(); - sel.clear(); if (selectionMode == SINGLE_SELECTION) - index0 = index1; - - int lo = Math.min(index0, index1); - int hi = Math.max(index0, index1); - sel.set(lo, hi+1); - // update the anchorSelectionIndex and leadSelectionIndex variables - setAnchorSelectionIndex(index0); - leadSelectionIndex=index1; + { + int lo = lead; + int hi = lead; + int selected = sel.nextSetBit(0); + if (selected == lead) + return; // the selection is not changing + if (selected >= 0) + { + lo = Math.min(lo, selected); + hi = Math.max(hi, selected); + } + if (anchorSelectionIndex >= 0) + { + lo = Math.min(lo, anchorSelectionIndex); + hi = Math.max(hi, anchorSelectionIndex); + } + sel.clear(); + sel.set(lead); + leadSelectionIndex = lead; + anchorSelectionIndex = lead; + fireValueChanged(lo, hi); + } + else if (selectionMode == SINGLE_INTERVAL_SELECTION) + { + // determine the current interval + int first = sel.nextSetBit(0); + int last = first; + if (first >= 0) + last += (sel.cardinality() - 1); + + // update the selection + int lo = Math.min(anchor, lead); + int hi = Math.max(anchor, lead); + if (lo == first && hi == last) + return; // selected interval is not being changed + sel.clear(); + sel.set(lo, hi + 1); + + // include the old selection in the event range + if (first >= 0) + lo = Math.min(lo, first); + if (last >= 0) + hi = Math.max(hi, last); + if (anchorSelectionIndex >= 0) + { + lo = Math.min(lo, anchorSelectionIndex); + hi = Math.max(hi, anchorSelectionIndex); + } + anchorSelectionIndex = anchor; + leadSelectionIndex = lead; + fireValueChanged(lo, hi); + } + else + { + BitSet oldSel = (BitSet) sel.clone(); + sel.clear(); + if (selectionMode == SINGLE_SELECTION) + anchor = lead; + + int lo = Math.min(anchor, lead); + int hi = Math.max(anchor, lead); + sel.set(lo, hi+1); + // update the anchorSelectionIndex and leadSelectionIndex variables + setAnchorSelectionIndex(anchor); + leadSelectionIndex = lead; - fireDifference(sel, oldSel); + fireDifference(sel, oldSel); + } } /** diff --git a/libjava/classpath/javax/swing/GrayFilter.java b/libjava/classpath/javax/swing/GrayFilter.java index b920b088a17..ef0d14718d6 100644 --- a/libjava/classpath/javax/swing/GrayFilter.java +++ b/libjava/classpath/javax/swing/GrayFilter.java @@ -76,9 +76,8 @@ public class GrayFilter extends RGBImageFilter */ public static Image createDisabledImage(Image src) { - return (Toolkit.getDefaultToolkit(). - createImage(new FilteredImageSource(src.getSource(), - new GrayFilter(true, 0)))); + return (Toolkit.getDefaultToolkit().createImage(new FilteredImageSource( + src.getSource(), new GrayFilter(true, 0)))); } /** diff --git a/libjava/classpath/javax/swing/InputMap.java b/libjava/classpath/javax/swing/InputMap.java index 28fccd9b9cd..19a75f4e985 100644 --- a/libjava/classpath/javax/swing/InputMap.java +++ b/libjava/classpath/javax/swing/InputMap.java @@ -1,5 +1,5 @@ /* InputMap.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,9 +37,6 @@ exception statement from your version. */ package javax.swing; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.HashMap; @@ -47,14 +44,13 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - /** * Maps {@link KeyStroke}s to arbitrary objects, usually Strings. This * is used in combination with {@link ActionMap}s. * * If a component receives an input event, this is looked up in * the component's <code>InputMap</code>. The result is an object which - * serves as a key to the components <code>ActionMap</code>. Finally + * serves as a key to the component's <code>ActionMap</code>. Finally * the <code>Action</code> that is stored is executed. * * @author Andrew Selkirk @@ -68,33 +64,37 @@ public class InputMap private static final long serialVersionUID = -5429059542008604257L; /** - * inputMap + * Storage for the KeyStroke --> Object mappings. */ - private Map inputMap = new HashMap(); + private Map inputMap; /** - * parent + * An optional parent map. */ private InputMap parent; /** - * Creates a new <code>InputMap</code> instance. + * Creates a new <code>InputMap</code> instance. This default instance + * contains no mappings and has no parent. */ public InputMap() { - // TODO + // nothing to do } /** - * Returns the binding for keystroke. + * Returns the binding for the specified keystroke, if there is one. * - * @param keystroke the key of the enty + * @param keystroke the key of the entry (<code>null</code> is ignored). * - * @return the binding associated with keystroke may be null + * @return The binding associated with the specified keystroke (or + * <code>null</code>). */ public Object get(KeyStroke keystroke) { - Object result = inputMap.get(keystroke); + Object result = null; + if (inputMap != null) + result = inputMap.get(keystroke); if (result == null && parent != null) result = parent.get(keystroke); @@ -102,14 +102,20 @@ public class InputMap } /** - * Puts a new entry into the <code>InputMap</code>. - * If actionMapKey is null an existing entry will be removed. + * Puts a new entry into the <code>InputMap</code>. If + * <code>actionMapKey</code> is <code>null</code> any existing entry will be + * removed. * - * @param keystroke the keystroke for the entry - * @param actionMapKey the action. + * @param keystroke the keystroke for the entry (<code>null</code> is + * ignored). + * @param actionMapKey the action (<code>null</code> permitted). */ public void put(KeyStroke keystroke, Object actionMapKey) { + if (keystroke == null) + return; + if (inputMap == null) + inputMap = new HashMap(); if (actionMapKey == null) inputMap.remove(keystroke); else @@ -117,19 +123,25 @@ public class InputMap } /** - * Remove an entry from the <code>InputMap</code>. + * Removes an entry from this <code>InputMap</code>. Note that this will + * not remove any entry from the parent map, if there is one. * - * @param keystroke the key of the entry to remove + * @param keystroke the key of the entry to remove (<code>null</code> is + * ignored). */ public void remove(KeyStroke keystroke) { - inputMap.remove(keystroke); + if (inputMap != null) + inputMap.remove(keystroke); } /** - * Returns the parent of this <code>InputMap</code>. + * Returns the parent of this <code>InputMap</code>. The default value + * is <code>null</code>. * - * @return the parent, may be null. + * @return The parent map (possibly <code>null</code>). + * + * @see #setParent(InputMap) */ public InputMap getParent() { @@ -137,9 +149,13 @@ public class InputMap } /** - * Sets a parent for this <code>InputMap</code>. + * Sets a parent for this <code>InputMap</code>. If a parent is specified, + * the {@link #get(KeyStroke)} method will look in the parent if it cannot + * find an entry in this map. * - * @param parentMap the new parent + * @param parentMap the new parent (<code>null</code> permitted). + * + * @see #getParent() */ public void setParent(InputMap parentMap) { @@ -147,31 +163,44 @@ public class InputMap } /** - * Returns the number of entries in this <code>InputMap</code>. + * Returns the number of entries in this <code>InputMap</code>. This count + * does not include any entries from the parent map, if there is one. * - * @return the number of entries + * @return The number of entries. */ public int size() { - return inputMap.size(); + int result = 0; + if (inputMap != null) + result = inputMap.size(); + return result; } /** - * Clears the <code>InputMap</code>. + * Clears the entries from this <code>InputMap</code>. The parent map, if + * there is one, is not cleared. */ public void clear() { - inputMap.clear(); + if (inputMap != null) + inputMap.clear(); } /** - * Returns all keys of entries in this <code>InputMap</code>. + * Returns all keys of entries in this <code>InputMap</code>. This does not + * include keys defined in the parent, if there is one (use the + * {@link #allKeys()} method for that case). + * <br><br> + * Following the behaviour of the reference implementation, this method will + * return <code>null</code> when no entries have been added to the map, + * and a zero length array if entries have been added but subsequently + * removed (or cleared) from the map. * - * @return an array of keys + * @return An array of keys (may be <code>null</code> or have zero length). */ public KeyStroke[] keys() { - if (size() != 0) + if (inputMap != null) { KeyStroke[] array = new KeyStroke[size()]; return (KeyStroke[]) inputMap.keySet().toArray(array); @@ -180,10 +209,10 @@ public class InputMap } /** - * Returns all keys of entries in this <code>InputMap</code> - * and all its parents. + * Returns all keys of entries in this <code>InputMap</code> and all its + * parents. * - * @return an array of keys + * @return An array of keys (may be <code>null</code> or have zero length). */ public KeyStroke[] allKeys() { @@ -195,36 +224,12 @@ public class InputMap if (parentKeys != null) set.addAll(Arrays.asList(parentKeys)); } - set.addAll(inputMap.keySet()); + if (inputMap != null) + set.addAll(inputMap.keySet()); if (set.size() == 0) return null; KeyStroke[] array = new KeyStroke[set.size()]; return (KeyStroke[]) set.toArray(array); } - /** - * writeObject - * - * @param stream the stream to write to - * - * @exception IOException If an error occurs - */ - private void writeObject(ObjectOutputStream stream) throws IOException - { - // TODO - } - - /** - * readObject - * - * @param stream the stream to read from - * - * @exception ClassNotFoundException If the serialized class cannot be found - * @exception IOException If an error occurs - */ - private void readObject(ObjectInputStream stream) - throws ClassNotFoundException, IOException - { - // TODO - } } diff --git a/libjava/classpath/javax/swing/JCheckBoxMenuItem.java b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java index 3222d189f37..51634c93f0b 100644 --- a/libjava/classpath/javax/swing/JCheckBoxMenuItem.java +++ b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java @@ -1,39 +1,39 @@ /* JCheckBoxMenuItem.java -- - Copyright (C) 2002, 2004, 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. */ + Copyright (C) 2002, 2004, 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 javax.swing; @@ -43,20 +43,20 @@ import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; /** - * A menu item that displays a checkbox. Its behaviour is very similar - * to {@link JCheckBox}. Just like the <code>JCheckBox</code>, user can check + * A menu item that displays a checkbox. Its behaviour is very similar to + * {@link JCheckBox}. Just like the <code>JCheckBox</code>, user can check * and uncheck this menu item by clicking on it. Also - * {@link AbstractButton#setSelected} and {@link #setState} can be use used - * for the same purpose. - * <code>JCheckBoxMenuItem</code> uses + * {@link AbstractButton#setSelected} and {@link #setState} can be use used for + * the same purpose. <code>JCheckBoxMenuItem</code> uses * <code>ToggleButtonModel</code> to keep track of its selection. - * + * * @author original author unknown */ -public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, - Accessible +public class JCheckBoxMenuItem + extends JMenuItem + implements SwingConstants, Accessible { - private static final long serialVersionUID = -6676402307973384715L; + private static final long serialVersionUID = - 6676402307973384715L; /** name for the UI delegate for this menuItem. */ private static final String uiClassID = "CheckBoxMenuItemUI"; @@ -65,8 +65,8 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, private boolean state; /** - * This array contains text of this menu item if this menu item is in - * checked state and null it is not. + * This array contains text of this menu item if this menu item is in checked + * state and null it is not. */ private Object[] selectedObjects = new Object[1]; @@ -80,7 +80,7 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Creates a new JCheckBoxMenuItem with given icon - * + * * @param icon Icon for this menu item */ public JCheckBoxMenuItem(Icon icon) @@ -90,7 +90,7 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Creates a new JCheckBoxMenuItem with given label - * + * * @param text Label for this menu item */ public JCheckBoxMenuItem(String text) @@ -100,7 +100,7 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Creates a new JCheckBoxMenuItem using given action - * + * * @param action Action for this menu item. */ public JCheckBoxMenuItem(Action action) @@ -111,7 +111,7 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Creates a new JCheckBoxMenuItem object with given label and icon - * + * * @param text Label for this menu item * @param icon Icon for this menu item */ @@ -121,12 +121,12 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * Creates a new JCheckBoxMenuItem object using specified label and - * marked as checked if given 'state' is true. - * + * Creates a new JCheckBoxMenuItem object using specified label and marked as + * checked if given 'state' is true. + * * @param text Label for this menu item - * @param state <code>true</code> if this item should be in checked state and - * <code>false</code> otherwise + * @param state <code>true</code> if this item should be in checked state + * and <code>false</code> otherwise */ public JCheckBoxMenuItem(String text, boolean state) { @@ -134,26 +134,28 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * Creates a new JCheckBoxMenuItem object with given label, icon, - * and marked as checked if given 'state' is true. - * + * Creates a new JCheckBoxMenuItem object with given label, icon, and marked + * as checked if given 'state' is true. + * * @param text Label for this menu item * @param icon icon for this menu item - * @param state <code>true</code> if this item should be in checked state and - * false otherwise + * @param state <code>true</code> if this item should be in checked state + * and false otherwise */ public JCheckBoxMenuItem(String text, Icon icon, boolean state) { super(text, icon); setModel(new JToggleButton.ToggleButtonModel()); this.state = state; - this.setVisible(true); + if (state == true) + this.setSelected(true); + setFocusable(false); } /** * This method returns a name to identify which look and feel class will be * the UI delegate for the menuItem. - * + * * @return The Look and Feel classID. "JCheckBoxMenuItemUI" */ public String getUIClassID() @@ -163,9 +165,9 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Returns checked state for this check box menu item. - * - * @return Returns true if this menu item is in checked state - * and false otherwise. + * + * @return Returns true if this menu item is in checked state and false + * otherwise. */ public boolean getState() { @@ -173,10 +175,9 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * Sets state for this check box menu item. If - * given 'state' is true, then mark menu item as checked, - * and uncheck this menu item otherwise. - * + * Sets state for this check box menu item. If given 'state' is true, then + * mark menu item as checked, and uncheck this menu item otherwise. + * * @param state new state for this menu item */ public synchronized void setState(boolean state) @@ -185,11 +186,11 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * This method returns array containing label of this - * menu item if it is selected and null otherwise. - * - * @return Array containing label of this - * menu item if this menu item is selected or null otherwise. + * This method returns array containing label of this menu item if it is + * selected and null otherwise. + * + * @return Array containing label of this menu item if this menu item is + * selected or null otherwise. */ public Object[] getSelectedObjects() { @@ -202,27 +203,26 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * This method overrides JComponent.requestFocus with an empty - * implementation, since JCheckBoxMenuItems should not - * receive focus in general. - */ + * This method overrides JComponent.requestFocus with an empty implementation, + * since JCheckBoxMenuItems should not receive focus in general. + */ public void requestFocus() { - // Should do nothing here + // Should do nothing here } /** - * Returns a string describing the attributes for the - * <code>JCheckBoxMenuItem</code> component, for use in debugging. The - * return value is guaranteed to be non-<code>null</code>, but the format + * Returns a string describing the attributes for the + * <code>JCheckBoxMenuItem</code> component, for use in debugging. The + * return value is guaranteed to be non-<code>null</code>, but the format * of the string may vary between implementations. - * - * @return A string describing the attributes of the - * <code>JCheckBoxMenuItem</code>. + * + * @return A string describing the attributes of the + * <code>JCheckBoxMenuItem</code>. */ protected String paramString() { - // calling super seems to be sufficient to match the reference + // calling super seems to be sufficient to match the reference // implementation here... return super.paramString(); } @@ -230,9 +230,9 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Returns the object that provides accessibility features for this * <code>JCheckBoxMenuItem</code> component. - * - * @return The accessible context (an instance of - * {@link AccessibleJCheckBoxMenuItem}). + * + * @return The accessible context (an instance of + * {@link AccessibleJCheckBoxMenuItem}). */ public AccessibleContext getAccessibleContext() { @@ -243,12 +243,13 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * Provides the accessibility features for the <code>JCheckBoxMenuItem</code> + * Provides the accessibility features for the <code>JCheckBoxMenuItem</code> * component. * * @see JCheckBoxMenuItem#getAccessibleContext() */ - protected class AccessibleJCheckBoxMenuItem extends AccessibleJMenuItem + protected class AccessibleJCheckBoxMenuItem + extends AccessibleJMenuItem { private static final long serialVersionUID = 1079958073579370777L; @@ -261,9 +262,9 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * Returns the accessible role for the <code>JCheckBoxMenuItem</code> + * Returns the accessible role for the <code>JCheckBoxMenuItem</code> * component. - * + * * @return {@link AccessibleRole#CHECK_BOX}. */ public AccessibleRole getAccessibleRole() diff --git a/libjava/classpath/javax/swing/JComboBox.java b/libjava/classpath/javax/swing/JComboBox.java index efb04592b50..c75a94bdc36 100644 --- a/libjava/classpath/javax/swing/JComboBox.java +++ b/libjava/classpath/javax/swing/JComboBox.java @@ -471,6 +471,7 @@ public class JComboBox extends JComponent implements ItemSelectable, public void setSelectedItem(Object item) { dataModel.setSelectedItem(item); + fireActionEvent(); } /** @@ -1028,7 +1029,8 @@ public class JComboBox extends JComponent implements ItemSelectable, } /** - * This method hides combo box's popup whenever TAB key is pressed. + * This method is fired whenever a key is pressed with the combo box + * in focus * * @param e The KeyEvent indicating which key was pressed. */ @@ -1036,15 +1038,6 @@ public class JComboBox extends JComponent implements ItemSelectable, { if (e.getKeyCode() == KeyEvent.VK_TAB) setPopupVisible(false); - else if (keySelectionManager != null) - { - int i = keySelectionManager.selectionForKey(e.getKeyChar(), - getModel()); - if (i >= 0) - setSelectedIndex(i); - else - super.processKeyEvent(e); - } else super.processKeyEvent(e); } @@ -1066,7 +1059,7 @@ public class JComboBox extends JComponent implements ItemSelectable, */ public KeySelectionManager getKeySelectionManager() { - return null; + return keySelectionManager; } /** @@ -1098,7 +1091,7 @@ public class JComboBox extends JComponent implements ItemSelectable, */ protected KeySelectionManager createDefaultKeySelectionManager() { - return null; + return new DefaultKeySelectionManager(); } /** @@ -1471,4 +1464,34 @@ public class JComboBox extends JComponent implements ItemSelectable, // Nothing to do here. } } + + private class DefaultKeySelectionManager + implements KeySelectionManager + { + + public int selectionForKey(char aKey, ComboBoxModel aModel) + { + int selectedIndex = getSelectedIndex(); + + // Start at currently selected item and iterate to end of list + for (int i = selectedIndex + 1; i < aModel.getSize(); i++) + { + String nextItem = aModel.getElementAt(i).toString(); + + if (nextItem.charAt(0) == aKey) + return i; + } + + // Wrap to start of list if no match yet + for (int i = 0; i <= selectedIndex; i++) + { + String nextItem = aModel.getElementAt(i).toString(); + + if (nextItem.charAt(0) == aKey) + return i; + } + + return - 1; + } + } } diff --git a/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java index 66c562d110b..fa83502946d 100644 --- a/libjava/classpath/javax/swing/JComponent.java +++ b/libjava/classpath/javax/swing/JComponent.java @@ -48,12 +48,10 @@ import java.awt.EventQueue; import java.awt.FocusTraversalPolicy; import java.awt.Font; import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; -import java.awt.Shape; import java.awt.Window; import java.awt.dnd.DropTarget; import java.awt.event.ActionEvent; @@ -69,8 +67,8 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; import java.io.Serializable; -import java.util.ArrayList; import java.util.EventListener; import java.util.Hashtable; import java.util.Locale; @@ -571,6 +569,21 @@ public abstract class JComponent extends Container implements Serializable */ String toolTipText; + /** + * The popup menu for the component. + * + * @see #getComponentPopupMenu() + * @see #setComponentPopupMenu(JPopupMenu) + */ + JPopupMenu componentPopupMenu; + + /** + * A flag that controls whether the {@link #getComponentPopupMenu()} method + * looks to the component's parent when the <code>componentPopupMenu</code> + * field is <code>null</code>. + */ + boolean inheritsPopupMenu; + /** * <p>Whether to double buffer this component when painting. This flag * should generally be <code>true</code>, to ensure good painting @@ -668,7 +681,13 @@ public abstract class JComponent extends Container implements Serializable * Indicates whether the current paint call is already double buffered or * not. */ - static boolean isPaintingDoubleBuffered = false; + static boolean paintingDoubleBuffered = false; + + /** + * Indicates whether we are calling paintDoubleBuffered() from + * paintImmadiately (RepaintManager) or from paint() (AWT refresh). + */ + static private boolean isRepainting = false; /** * Listeners for events other than {@link PropertyChangeEvent} are @@ -677,6 +696,11 @@ public abstract class JComponent extends Container implements Serializable */ protected EventListenerList listenerList = new EventListenerList(); + /** + * Handles VetoableChangeEvents. + */ + private VetoableChangeSupport vetoableChangeSupport; + /** * Storage for "client properties", which are key/value pairs associated * with this component by a "client", such as a user application or a @@ -690,7 +714,7 @@ public abstract class JComponent extends Container implements Serializable private ComponentInputMap inputMap_whenInFocusedWindow; private ActionMap actionMap; /** @since 1.3 */ - private boolean verifyInputWhenFocusTarget; + private boolean verifyInputWhenFocusTarget = true; private InputVerifier inputVerifier; private TransferHandler transferHandler; @@ -784,7 +808,7 @@ public abstract class JComponent extends Container implements Serializable { super(); setDropTarget(new DropTarget()); - defaultLocale = Locale.getDefault(); + setLocale(getDefaultLocale()); debugGraphicsOptions = DebugGraphics.NONE_OPTION; setRequestFocusEnabled(true); } @@ -868,7 +892,8 @@ public abstract class JComponent extends Container implements Serializable */ public void removeVetoableChangeListener(VetoableChangeListener listener) { - listenerList.remove(VetoableChangeListener.class, listener); + if (vetoableChangeSupport != null) + vetoableChangeSupport.removeVetoableChangeListener(listener); } /** @@ -884,23 +909,6 @@ public abstract class JComponent extends Container implements Serializable } /** - * Register a <code>PropertyChangeListener</code> for a specific, named - * property. To listen to all property changes, regardless of name, use - * {@link #addPropertyChangeListener(PropertyChangeListener)} instead. - * - * @param propertyName The property name to listen to - * @param listener The listener to register - * - * @see #removePropertyChangeListener(String, PropertyChangeListener) - * @see #changeSupport - */ - public void addPropertyChangeListener(String propertyName, - PropertyChangeListener listener) - { - listenerList.add(PropertyChangeListener.class, listener); - } - - /** * Register a <code>VetoableChangeListener</code>. * * @param listener The listener to register @@ -910,7 +918,10 @@ public abstract class JComponent extends Container implements Serializable */ public void addVetoableChangeListener(VetoableChangeListener listener) { - listenerList.add(VetoableChangeListener.class, listener); + // Lazily instantiate this, it's rarely needed. + if (vetoableChangeSupport == null) + vetoableChangeSupport = new VetoableChangeSupport(this); + vetoableChangeSupport.addVetoableChangeListener(listener); } /** @@ -936,6 +947,8 @@ public abstract class JComponent extends Container implements Serializable { if (listenerType == PropertyChangeListener.class) return getPropertyChangeListeners(); + else if (listenerType == VetoableChangeListener.class) + return getVetoableChangeListeners(); else return listenerList.getListeners(listenerType); } @@ -954,84 +967,89 @@ public abstract class JComponent extends Container implements Serializable /** * Return all registered <code>VetoableChangeListener</code> objects. * - * @return The set of <code>VetoableChangeListener</code> objects in {@link - * #listenerList} + * @return An array of the <code>VetoableChangeListener</code> objects + * registered with this component (possibly empty but never + * <code>null</code>). + * + * @since 1.4 */ public VetoableChangeListener[] getVetoableChangeListeners() - { - return (VetoableChangeListener[]) getListeners(VetoableChangeListener.class); + { + return vetoableChangeSupport == null ? new VetoableChangeListener[0] + : vetoableChangeSupport.getVetoableChangeListeners(); } /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>boolean</code> values. + * Call {@link VetoableChangeListener#vetoableChange} on all listeners + * registered to listen to a given property. Any method which changes + * the specified property of this component should call this method. + * + * @param propertyName The property which changed + * @param oldValue The old value of the property + * @param newValue The new value of the property + * + * @throws PropertyVetoException if the change was vetoed by a listener * - * @specnote It seems that in JDK1.5 all property related methods have been - * moved to java.awt.Component, except this and 2 others. We call - * super here. I guess this will also be removed in one of the next - * releases. + * @see #addVetoableChangeListener + * @see #removeVetoableChangeListener */ - public void firePropertyChange(String propertyName, boolean oldValue, - boolean newValue) + protected void fireVetoableChange(String propertyName, Object oldValue, + Object newValue) + throws PropertyVetoException { - super.firePropertyChange(propertyName, oldValue, newValue); + if (vetoableChangeSupport != null) + vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue); } + /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>char</code> values. + * Fires a property change for a primitive integer property. + * + * @param property the name of the property + * @param oldValue the old value of the property + * @param newValue the new value of the property * - * @specnote It seems that in JDK1.5 all property related methods have been - * moved to java.awt.Component, except this and 2 others. We call - * super here. I guess this will also be removed in one of the next - * releases. + * @specnote This method is implemented in + * {@link Component#firePropertyChange(String, int, int)}. It is + * only here because it is specified to be public, whereas the + * Component method is protected. */ - public void firePropertyChange(String propertyName, char oldValue, - char newValue) + public void firePropertyChange(String property, int oldValue, int newValue) { - super.firePropertyChange(propertyName, oldValue, newValue); + super.firePropertyChange(property, oldValue, newValue); } - + /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>int</code> values. + * Fires a property change for a primitive boolean property. + * + * @param property the name of the property + * @param oldValue the old value of the property + * @param newValue the new value of the property * - * @specnote It seems that in JDK1.5 all property related methods have been - * moved to java.awt.Component, except this and 2 others. We call - * super here. I guess this will also be removed in one of the next - * releases. + * @specnote This method is implemented in + * {@link Component#firePropertyChange(String, boolean, boolean)}. + * It is only here because it is specified to be public, whereas + * the Component method is protected. */ - public void firePropertyChange(String propertyName, int oldValue, - int newValue) + public void firePropertyChange(String property, boolean oldValue, + boolean newValue) { - super.firePropertyChange(propertyName, oldValue, newValue); + super.firePropertyChange(property, oldValue, newValue); } /** - * Call {@link VetoableChangeListener#vetoableChange} on all listeners - * registered to listen to a given property. Any method which changes - * the specified property of this component should call this method. - * - * @param propertyName The property which changed - * @param oldValue The old value of the property - * @param newValue The new value of the property - * - * @throws PropertyVetoException if the change was vetoed by a listener + * Fires a property change for a primitive character property. * - * @see #addVetoableChangeListener - * @see #removeVetoableChangeListener + * @param property the name of the property + * @param oldValue the old value of the property + * @param newValue the new value of the property */ - protected void fireVetoableChange(String propertyName, Object oldValue, - Object newValue) - throws PropertyVetoException + public void firePropertyChange(String property, char oldValue, + char newValue) { - VetoableChangeListener[] listeners = getVetoableChangeListeners(); - - PropertyChangeEvent evt = - new PropertyChangeEvent(this, propertyName, oldValue, newValue); - - for (int i = 0; i < listeners.length; i++) - listeners[i].vetoableChange(evt); + // FIXME - This method is already public in awt Component, but + // is included here to work around a compilation bug in gcj 4.1. + super.firePropertyChange(property, oldValue, newValue); } /** @@ -1402,11 +1420,32 @@ public abstract class JComponent extends Container implements Serializable * Return the set of {@link KeyStroke} objects which are registered * to initiate actions on this component. * - * @return An array of the registered keystrokes + * @return An array of the registered keystrokes (possibly empty but never + * <code>null</code>). */ public KeyStroke[] getRegisteredKeyStrokes() { - return null; + KeyStroke[] ks0; + KeyStroke[] ks1; + KeyStroke[] ks2; + if (inputMap_whenFocused != null) + ks0 = inputMap_whenFocused.keys(); + else + ks0 = new KeyStroke[0]; + if (inputMap_whenAncestorOfFocused != null) + ks1 = inputMap_whenAncestorOfFocused.keys(); + else + ks1 = new KeyStroke[0]; + if (inputMap_whenInFocusedWindow != null) + ks2 = inputMap_whenInFocusedWindow.keys(); + else + ks2 = new KeyStroke[0]; + int count = ks0.length + ks1.length + ks2.length; + KeyStroke[] result = new KeyStroke[count]; + System.arraycopy(ks0, 0, result, 0, ks0.length); + System.arraycopy(ks1, 0, result, ks0.length, ks1.length); + System.arraycopy(ks2, 0, result, ks0.length + ks1.length, ks2.length); + return result; } /** @@ -1524,8 +1563,90 @@ public abstract class JComponent extends Container implements Serializable { return getToolTipText(); } + + /** + * Returns the flag that controls whether or not the component inherits its + * parent's popup menu when no popup menu is specified for this component. + * + * @return A boolean. + * + * @since 1.5 + * + * @see #setInheritsPopupMenu(boolean) + */ + public boolean getInheritsPopupMenu() + { + return inheritsPopupMenu; + } + + /** + * Sets the flag that controls whether or not the component inherits its + * parent's popup menu when no popup menu is specified for this component. + * This is a bound property with the property name 'inheritsPopupMenu'. + * + * @param inherit the new flag value. + * + * @since 1.5 + * + * @see #getInheritsPopupMenu() + */ + public void setInheritsPopupMenu(boolean inherit) + { + if (inheritsPopupMenu != inherit) + { + inheritsPopupMenu = inherit; + this.firePropertyChange("inheritsPopupMenu", ! inherit, inherit); + } + } + + /** + * Returns the popup menu for this component. If the popup menu is + * <code>null</code> AND the {@link #getInheritsPopupMenu()} method returns + * <code>true</code>, this method will return the parent's popup menu (if it + * has one). + * + * @return The popup menu (possibly <code>null</code>. + * + * @since 1.5 + * + * @see #setComponentPopupMenu(JPopupMenu) + * @see #getInheritsPopupMenu() + */ + public JPopupMenu getComponentPopupMenu() + { + if (componentPopupMenu == null && getInheritsPopupMenu()) + { + Container parent = getParent(); + if (parent instanceof JComponent) + return ((JComponent) parent).getComponentPopupMenu(); + else + return null; + } + else + return componentPopupMenu; + } /** + * Sets the popup menu for this component (this is a bound property with + * the property name 'componentPopupMenu'). + * + * @param popup the popup menu (<code>null</code> permitted). + * + * @since 1.5 + * + * @see #getComponentPopupMenu() + */ + public void setComponentPopupMenu(JPopupMenu popup) + { + if (componentPopupMenu != popup) + { + JPopupMenu old = componentPopupMenu; + componentPopupMenu = popup; + firePropertyChange("componentPopupMenu", old, popup); + } + } + + /** * Return the top level ancestral container (usually a {@link * java.awt.Window} or {@link java.applet.Applet}) which this component is * contained within, or <code>null</code> if no ancestors exist. @@ -1725,7 +1846,7 @@ public abstract class JComponent extends Container implements Serializable // buffer. When this method completes, the call stack unwinds back to // paintDoubleBuffered, where the buffer contents is finally drawn to the // screen. - if (!isPaintingDoubleBuffered && isDoubleBuffered() + if (!paintingDoubleBuffered && isDoubleBuffered() && rm.isDoubleBufferingEnabled()) { Rectangle clip = g.getClipBounds(); @@ -1816,235 +1937,77 @@ public abstract class JComponent extends Container implements Serializable { if (getComponentCount() > 0) { - if (isOptimizedDrawingEnabled()) - paintChildrenOptimized(g); - else - paintChildrenWithOverlap(g); - } - } - - /** - * Paints the children of this JComponent in the case when the component - * is not marked as optimizedDrawingEnabled, that means the container cannot - * guarantee that it's children are tiled. For this case we must - * perform a more complex optimization to determine the minimal rectangle - * to be painted for each child component. - * - * @param g the graphics context to use - */ - private void paintChildrenWithOverlap(Graphics g) - { - Shape originalClip = g.getClip(); - Rectangle inner = SwingUtilities.calculateInnerArea(this, rectCache); - g.clipRect(inner.x, inner.y, inner.width, inner.height); - Component[] children = getComponents(); - - // Find the rectangles that need to be painted for each child component. - // We push on this list arrays that have the Rectangles to be painted as - // the first elements and the component to be painted as the last one. - // Later we go through that list in reverse order and paint the rectangles. - ArrayList paintRegions = new ArrayList(children.length); - ArrayList paintRectangles = new ArrayList(); - ArrayList newPaintRects = new ArrayList(); - paintRectangles.add(g.getClipBounds()); - ArrayList componentRectangles = new ArrayList(); - - // Go through children from top to bottom and find out their paint - // rectangles. - for (int index = 0; paintRectangles.size() > 0 && - index < children.length; index++) - { - Component comp = children[index]; - if (! comp.isVisible()) - continue; - - Rectangle compBounds = comp.getBounds(); - boolean isOpaque = comp instanceof JComponent - && ((JComponent) comp).isOpaque(); - - // Add all the current paint rectangles that intersect with the - // component to the component's paint rectangle array. - for (int i = paintRectangles.size() - 1; i >= 0; i--) + // Need to lock the tree to avoid problems with AWT and concurrency. + synchronized (getTreeLock()) { - Rectangle r = (Rectangle) paintRectangles.get(i); - if (r.intersects(compBounds)) + for (int i = getComponentCount() - 1; i >= 0; i--) { - Rectangle compRect = r.intersection(compBounds); - componentRectangles.add(compRect); - // If the component is opaque, split up each paint rect and - // add paintRect - compBounds to the newPaintRects array. - if (isOpaque) + Component child = getComponent(i); + if (child != null && child.isLightweight() + && child.isVisible()) { - int x, y, w, h; - Rectangle rect = new Rectangle(); - - // The north retangle. - x = Math.max(compBounds.x, r.x); - y = r.y; - w = Math.min(compBounds.width, r.width + r.x - x); - h = compBounds.y - r.y; - rect.setBounds(x, y, w, h); - if (! rect.isEmpty()) - { - newPaintRects.add(rect); - rect = new Rectangle(); - } - - // The south rectangle. - x = Math.max(compBounds.x, r.x); - y = compBounds.y + compBounds.height; - w = Math.min(compBounds.width, r.width + r.x - x); - h = r.height - (compBounds.y - r.y) - compBounds.height; - rect.setBounds(x, y, w, h); - if (! rect.isEmpty()) - { - newPaintRects.add(rect); - rect = new Rectangle(); - } - - // The west rectangle. - x = r.x; - y = r.y; - w = compBounds.x - r.x; - h = r.height; - rect.setBounds(x, y, w, h); - if (! rect.isEmpty()) + int cx = child.getX(); + int cy = child.getY(); + int cw = child.getWidth(); + int ch = child.getHeight(); + if (g.hitClip(cx, cy, cw, ch)) { - newPaintRects.add(rect); - rect = new Rectangle(); - } - - // The east rectangle. - x = compBounds.x + compBounds.width; - y = r.y; - w = r.width - (compBounds.x - r.x) - compBounds.width; - h = r.height; - rect.setBounds(x, y, w, h); - if (! rect.isEmpty()) - { - newPaintRects.add(rect); + if ((! isOptimizedDrawingEnabled()) && i > 0) + { + // Check if the child is completely obscured. + Rectangle clip = g.getClipBounds(); // A copy. + SwingUtilities.computeIntersection(cx, cy, cw, ch, + clip); + if (isCompletelyObscured(i, clip)) + continue; // Continues the for-loop. + } + Graphics cg = g.create(cx, cy, cw, ch); + cg.setColor(child.getForeground()); + cg.setFont(child.getFont()); + try + { + child.paint(cg); + } + finally + { + cg.dispose(); + } } } - else - { - // Not opaque, need to reuse the current paint rectangles - // for the next component. - newPaintRects.add(r); - } - - } - else - { - newPaintRects.add(r); } } - - // Replace the paintRectangles with the new split up - // paintRectangles. - paintRectangles.clear(); - paintRectangles.addAll(newPaintRects); - newPaintRects.clear(); - - // Store paint rectangles if there are any for the current component. - int compRectsSize = componentRectangles.size(); - if (compRectsSize > 0) - { - componentRectangles.add(comp); - paintRegions.add(componentRectangles); - componentRectangles = new ArrayList(); - } } - - // paintingTile becomes true just before we start painting the component's - // children. - paintingTile = true; - - // We must go through the painting regions backwards, because the - // topmost components have been added first, followed by the components - // below. - int prEndIndex = paintRegions.size() - 1; - for (int i = prEndIndex; i >= 0; i--) - { - // paintingTile must be set to false before we begin to start painting - // the last tile. - if (i == 0) - paintingTile = false; - - ArrayList paintingRects = (ArrayList) paintRegions.get(i); - // The last element is always the component. - Component c = (Component) paintingRects.get(paintingRects.size() - 1); - int endIndex = paintingRects.size() - 2; - for (int j = 0; j <= endIndex; j++) - { - Rectangle cBounds = c.getBounds(); - Rectangle bounds = (Rectangle) paintingRects.get(j); - Rectangle oldClip = g.getClipBounds(); - if (oldClip == null) - oldClip = bounds; - - boolean translated = false; - try - { - g.setClip(bounds); - g.translate(cBounds.x, cBounds.y); - translated = true; - c.paint(g); - } - finally - { - if (translated) - g.translate(-cBounds.x, -cBounds.y); - g.setClip(oldClip); - } - } - } - g.setClip(originalClip); } /** - * Paints the children of this container when it is marked as - * optimizedDrawingEnabled. In this case the container can guarantee that - * it's children are tiled, which allows for a much more efficient - * algorithm to determine the minimum rectangles to be painted for - * each child. + * Determines if a region of a child component is completely obscured by one + * of its siblings. + * + * @param index the index of the child component + * @param rect the region to check * - * @param g the graphics context to use + * @return <code>true</code> if the region is completely obscured by a + * sibling, <code>false</code> otherwise */ - private void paintChildrenOptimized(Graphics g) + private boolean isCompletelyObscured(int index, Rectangle rect) { - Shape originalClip = g.getClip(); - Rectangle inner = SwingUtilities.calculateInnerArea(this, rectCache); - g.clipRect(inner.x, inner.y, inner.width, inner.height); - Component[] children = getComponents(); - - // paintingTile becomes true just before we start painting the component's - // children. - paintingTile = true; - for (int i = children.length - 1; i >= 0; i--) //children.length; i++) + boolean obscured = false; + for (int i = index - 1; i >= 0 && obscured == false; i--) { - // paintingTile must be set to false before we begin to start painting - // the last tile. - if (i == children.length - 1) - paintingTile = false; - - if (!children[i].isVisible()) - continue; - - Rectangle bounds = children[i].getBounds(rectCache); - Rectangle oldClip = g.getClipBounds(); - if (oldClip == null) - oldClip = bounds; - - if (!g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) - continue; - - boolean translated = false; - Graphics g2 = g.create(bounds.x, bounds.y, bounds.width, - bounds.height); - children[i].paint(g2); - g2.dispose(); + Component sib = getComponent(i); + if (sib.isVisible()) + { + Rectangle sibRect = sib.getBounds(rectCache); + if (sib.isOpaque() && rect.x >= sibRect.x + && (rect.x + rect.width) <= (sibRect.x + sibRect.width) + && rect.y >= sibRect.y + && (rect.y + rect.height) <= (sibRect.y + sibRect.height)) + { + obscured = true; + } + } } - g.setClip(originalClip); + return obscured; } /** @@ -2064,12 +2027,15 @@ public abstract class JComponent extends Container implements Serializable { if (ui != null) { - Graphics g2 = g; - if (!(g instanceof Graphics2D)) - g2 = g.create(); - ui.update(g2, this); - if (!(g instanceof Graphics2D)) - g2.dispose(); + Graphics g2 = g.create(); + try + { + ui.update(g2, this); + } + finally + { + g2.dispose(); + } } } @@ -2112,16 +2078,13 @@ public abstract class JComponent extends Container implements Serializable Component root = findPaintRoot(r); // If no paint root is found, then this component is completely overlapped // by another component and we don't need repainting. - if (root == null) + if (root == null|| !root.isShowing()) return; - if (root == null || !root.isShowing()) - return; - - Rectangle rootClip = SwingUtilities.convertRectangle(this, r, root); + SwingUtilities.convertRectangleToAncestor(this, r, root); if (root instanceof JComponent) - ((JComponent) root).paintImmediately2(rootClip); + ((JComponent) root).paintImmediately2(r); else - root.repaint(rootClip.x, rootClip.y, rootClip.width, rootClip.height); + root.repaint(r.x, r.y, r.width, r.height); } /** @@ -2131,40 +2094,35 @@ public abstract class JComponent extends Container implements Serializable */ void paintImmediately2(Rectangle r) { + isRepainting = true; RepaintManager rm = RepaintManager.currentManager(this); - if (rm.isDoubleBufferingEnabled() && isDoubleBuffered()) + if (rm.isDoubleBufferingEnabled() && isPaintingDoubleBuffered()) paintDoubleBuffered(r); else paintSimple(r); + isRepainting = false; } /** - * Gets the root of the component given. If a parent of the - * component is an instance of Applet, then the applet is - * returned. The applet is considered the root for painting - * and adding/removing components. Otherwise, the root Window - * is returned if it exists. - * - * @param comp - The component to get the root for. - * @return the parent root. An applet if it is a parent, - * or the root window. If neither exist, null is returned. + * Returns true if we must paint double buffered, that is, when this + * component or any of it's ancestors are double buffered. + * + * @return true if we must paint double buffered, that is, when this + * component or any of it's ancestors are double buffered */ - private Component getRoot(Component comp) + private boolean isPaintingDoubleBuffered() { - Applet app = null; - - while (comp != null) - { - if (app == null && comp instanceof Window) - return comp; - else if (comp instanceof Applet) - app = (Applet) comp; - comp = comp.getParent(); - } - - return app; + boolean doubleBuffered = isDoubleBuffered(); + Component parent = getParent(); + while (! doubleBuffered && parent != null) + { + doubleBuffered = parent instanceof JComponent + && ((JComponent) parent).isDoubleBuffered(); + parent = parent.getParent(); + } + return doubleBuffered; } - + /** * Performs double buffered repainting. */ @@ -2173,7 +2131,7 @@ public abstract class JComponent extends Container implements Serializable RepaintManager rm = RepaintManager.currentManager(this); // Paint on the offscreen buffer. - Component root = getRoot(this); + Component root = SwingUtilities.getRoot(this); Image buffer = rm.getVolatileOffscreenBuffer(this, root.getWidth(), root.getHeight()); @@ -2183,26 +2141,30 @@ public abstract class JComponent extends Container implements Serializable buffer = rm.getOffscreenBuffer(this, root.getWidth(), root.getHeight()); //Rectangle targetClip = SwingUtilities.convertRectangle(this, r, root); - Point translation = SwingUtilities.convertPoint(this, 0, 0, root); Graphics g2 = buffer.getGraphics(); clipAndTranslateGraphics(root, this, g2); g2.clipRect(r.x, r.y, r.width, r.height); g2 = getComponentGraphics(g2); - isPaintingDoubleBuffered = true; + paintingDoubleBuffered = true; try { - paint(g2); + if (isRepainting) // Called from paintImmediately, go through paint(). + paint(g2); + else // Called from paint() (AWT refresh), don't call it again. + { + paintComponent(g2); + paintBorder(g2); + paintChildren(g2); + } } finally { - isPaintingDoubleBuffered = false; + paintingDoubleBuffered = false; g2.dispose(); } // Paint the buffer contents on screen. - rm.commitBuffer(root, new Rectangle(translation.x + r.x, - translation.y + r.y, r.width, - r.height)); + rm.commitBuffer(this, r); } /** @@ -2217,11 +2179,16 @@ public abstract class JComponent extends Container implements Serializable private void clipAndTranslateGraphics(Component root, Component target, Graphics g) { - Component parent = target.getParent(); - if (parent != root) - clipAndTranslateGraphics(root, parent, g); - - g.translate(target.getX(), target.getY()); + Component parent = target; + int deltaX = 0; + int deltaY = 0; + while (parent != root) + { + deltaX += parent.getX(); + deltaY += parent.getY(); + parent = parent.getParent(); + } + g.translate(deltaX, deltaY); g.clipRect(0, 0, target.getWidth(), target.getHeight()); } @@ -2271,7 +2238,13 @@ public abstract class JComponent extends Container implements Serializable /** * A variant of {@link * #registerKeyboardAction(ActionListener,String,KeyStroke,int)} which - * provides <code>null</code> for the command name. + * provides <code>null</code> for the command name. + * + * @param act the action listener to notify when the keystroke occurs. + * @param stroke the key stroke. + * @param cond the condition (one of {@link #WHEN_FOCUSED}, + * {@link #WHEN_IN_FOCUSED_WINDOW} and + * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}). */ public void registerKeyboardAction(ActionListener act, KeyStroke stroke, @@ -2348,9 +2321,22 @@ public abstract class JComponent extends Container implements Serializable KeyStroke stroke, int cond) { - getInputMap(cond).put(stroke, new ActionListenerProxy(act, cmd)); + ActionListenerProxy proxy = new ActionListenerProxy(act, cmd); + getInputMap(cond).put(stroke, proxy); + getActionMap().put(proxy, proxy); } + /** + * Sets the input map for the given condition. + * + * @param condition the condition (one of {@link #WHEN_FOCUSED}, + * {@link #WHEN_IN_FOCUSED_WINDOW} and + * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}). + * @param map the map. + * + * @throws IllegalArgumentException if <code>condition</code> is not one of + * the specified values. + */ public final void setInputMap(int condition, InputMap map) { enableEvents(AWTEvent.KEY_EVENT_MASK); @@ -2486,13 +2472,17 @@ public abstract class JComponent extends Container implements Serializable */ public ActionListener getActionForKeyStroke(KeyStroke ks) { - Object cmd = getInputMap().get(ks); - if (cmd != null) + Object key = getInputMap(JComponent.WHEN_FOCUSED).get(ks); + if (key == null) + key = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).get(ks); + if (key == null) + key = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).get(ks); + if (key != null) { - if (cmd instanceof ActionListenerProxy) - return (ActionListenerProxy) cmd; - else if (cmd instanceof String) - return getActionMap().get(cmd); + if (key instanceof ActionListenerProxy) + return ((ActionListenerProxy) key).target; + else + return getActionMap().get(key); } return null; } @@ -2591,10 +2581,11 @@ public abstract class JComponent extends Container implements Serializable if (isEnabled()) { Action act = null; + Object cmd = null; InputMap map = getInputMap(condition); if (map != null) { - Object cmd = map.get(ks); + cmd = map.get(ks); if (cmd != null) { if (cmd instanceof ActionListenerProxy) @@ -2604,7 +2595,23 @@ public abstract class JComponent extends Container implements Serializable } } if (act != null && act.isEnabled()) - return SwingUtilities.notifyAction(act, ks, e, this, e.getModifiers()); + { + // Need to synchronize here so we don't get in trouble with + // our __command__ hack. + synchronized (act) + { + // We add the command as value to the action, so that + // the action can later determine the command with which it + // was called. This is undocumented, but shouldn't affect + // compatibility. It allows us to use only one Action instance + // to do the work for all components of one type, instead of + // having loads of small Actions. This effectivly saves startup + // time of Swing. + act.putValue("__command__", cmd); + return SwingUtilities.notifyAction(act, ks, e, this, + e.getModifiers()); + } + } } return false; } @@ -2706,6 +2713,11 @@ public abstract class JComponent extends Container implements Serializable */ public void revalidate() { + // As long as we don't have a parent we don't need to do any layout, since + // this is done anyway as soon as we get connected to a parent. + if (getParent() == null) + return; + if (! EventQueue.isDispatchThread()) SwingUtilities.invokeLater(new Runnable() { @@ -3089,11 +3101,29 @@ public abstract class JComponent extends Container implements Serializable // Nothing to do here. } + /** + * Returns the locale used as the default for all new components. The + * default value is {@link Locale#getDefault()} (that is, the platform + * default locale). + * + * @return The locale (never <code>null</code>). + * + * @see #setDefaultLocale(Locale) + */ public static Locale getDefaultLocale() { + if (defaultLocale == null) + defaultLocale = Locale.getDefault(); return defaultLocale; } + /** + * Sets the locale to be used as the default for all new components. If this + * is set to <code>null</code>, the {@link #getDefaultLocale()} method will + * return the platform default locale. + * + * @param l the locale (<code>null</code> permitted). + */ public static void setDefaultLocale(Locale l) { defaultLocale = l; @@ -3531,12 +3561,13 @@ public abstract class JComponent extends Container implements Serializable } } // Dispatch event to all children. - Component[] children = getComponents(); - for (int i = 0; i < children.length; i++) + int numChildren = getComponentCount(); + for (int i = 0; i < numChildren; i++) { - if (!(children[i] instanceof JComponent)) + Component child = getComponent(i); + if (! (child instanceof JComponent)) continue; - JComponent jc = (JComponent) children[i]; + JComponent jc = (JComponent) child; jc.fireAncestorEvent(ancestor, id); } } @@ -3619,10 +3650,10 @@ public abstract class JComponent extends Container implements Serializable ! SwingUtilities.isRectangleContainingRectangle(parentRect, target); if (! haveOverlap) { - Component[] children = newParent.getComponents(); - for (int i = 0; children[i] != parent && !haveOverlap; i++) + Component child; + for (int i = 0; (child = newParent.getComponent(i)) != parent && !haveOverlap; i++) { - Rectangle childRect = children[i].getBounds(); + Rectangle childRect = child.getBounds(); haveOverlap = target.intersects(childRect); } } @@ -3654,7 +3685,7 @@ public abstract class JComponent extends Container implements Serializable { if ((found instanceof JComponent) && ((JComponent) found).isOpaque()) break; - else if (!(found instanceof JComponent)) + else if (!(found instanceof JComponent) && !found.isLightweight()) break; Container p = found.getParent(); if (p == null) diff --git a/libjava/classpath/javax/swing/JFileChooser.java b/libjava/classpath/javax/swing/JFileChooser.java index 64f9bd0f4e6..a508b8fcb20 100644 --- a/libjava/classpath/javax/swing/JFileChooser.java +++ b/libjava/classpath/javax/swing/JFileChooser.java @@ -43,6 +43,8 @@ import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowAdapter; import java.beans.PropertyChangeEvent; import java.io.File; import java.util.ArrayList; @@ -351,7 +353,7 @@ public class JFileChooser extends JComponent implements Accessible * The file selection mode. * @see #setFileSelectionMode(int) */ - private int fileSelectionMode = FILES_AND_DIRECTORIES; + private int fileSelectionMode = FILES_ONLY; /** * The file view. @@ -744,10 +746,16 @@ public class JFileChooser extends JComponent implements Accessible JDialog dialog = new JDialog(toUse); setSelectedFile(null); dialog.getContentPane().add(this); + dialog.addWindowListener( new WindowAdapter() + { + public void windowClosing(WindowEvent e) + { + cancelSelection(); + } + }); dialog.setModal(true); dialog.invalidate(); dialog.repaint(); - return dialog; } diff --git a/libjava/classpath/javax/swing/JInternalFrame.java b/libjava/classpath/javax/swing/JInternalFrame.java index 79dcc7326be..ff3ae1faa66 100644 --- a/libjava/classpath/javax/swing/JInternalFrame.java +++ b/libjava/classpath/javax/swing/JInternalFrame.java @@ -38,9 +38,11 @@ exception statement from your version. */ package javax.swing; +import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Graphics; +import java.awt.IllegalComponentStateException; import java.awt.KeyboardFocusManager; import java.awt.LayoutManager; import java.awt.Rectangle; @@ -490,12 +492,6 @@ public class JInternalFrame extends JComponent implements Accessible, /** Whether the JInternalFrame has become visible for the very first time. */ private transient boolean isFirstTimeVisible = true; - /** - * Whether the JInternalFrame is in the transition from being a maximized - * frame back to a regular sized frame. - */ - private transient boolean maxTransition = false; - /** DOCUMENT ME! */ private transient boolean wasIcon = false; @@ -581,11 +577,12 @@ public class JInternalFrame extends JComponent implements Accessible, this.closable = closable; this.maximizable = maximizable; this.iconable = iconifiable; - storedBounds = new Rectangle(); + isMaximum = false; setRootPane(createRootPane()); // JInternalFrames are invisible and opaque by default. setVisible(false); setOpaque(true); + desktopIcon = new JDesktopIcon(this); updateUI(); setRootPaneCheckingEnabled(true); // Done the init stage, now adds go to content pane. } @@ -643,24 +640,25 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void dispose() { - setVisible(false); - JDesktopPane pane = getDesktopPane(); - if (pane != null) - pane.setSelectedFrame(null); - else + if (isVisible()) + setVisible(false); + if (isSelected()) { - try - { - setSelected(false); - } - catch (PropertyVetoException e) - { - // Do nothing if they don't want to be unselected. - } + try + { + setSelected(false); + } + catch (PropertyVetoException e) + { + // Do nothing if they don't want to be unselected. + } + } + if (! isClosed) + { + firePropertyChange(IS_CLOSED_PROPERTY, Boolean.FALSE, Boolean.TRUE); + isClosed = true; } - isClosed = true; fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSED); - removeNotify(); } /** @@ -799,8 +797,6 @@ public class JInternalFrame extends JComponent implements Accessible, */ public JDesktopIcon getDesktopIcon() { - if (desktopIcon == null) - desktopIcon = new JDesktopIcon(this); return desktopIcon; } @@ -904,18 +900,7 @@ public class JInternalFrame extends JComponent implements Accessible, */ public int getLayer() { - JDesktopPane pane = getDesktopPane(); - if (pane != null) - // The cast here forces the call to the instance method getLayer() - // instead of the static method (this would lead to infinite - // recursion). - return pane.getLayer((Component) this); - - Integer layer = (Integer) getClientProperty(JLayeredPane.LAYER_PROPERTY); - if (layer != null) - return layer.intValue(); - - return JLayeredPane.DEFAULT_LAYER.intValue(); + return JLayeredPane.getLayer(this); } /** @@ -970,7 +955,7 @@ public class JInternalFrame extends JComponent implements Accessible, */ public Rectangle getNormalBounds() { - if (! isMaximum() && ! maxTransition) + if (storedBounds == null) return getBounds(); else return storedBounds; @@ -1035,20 +1020,8 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void hide() { - JDesktopPane pane = getDesktopPane(); - if (pane != null) - pane.setSelectedFrame(null); - else - { - try - { - setSelected(false); - } - catch (PropertyVetoException e) - { - // Do nothing. - } - } + if (isIcon()) + getDesktopIcon().hide(); super.hide(); } @@ -1162,8 +1135,9 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void moveToBack() { - if (getParent() instanceof JLayeredPane) - ((JLayeredPane) getParent()).moveToBack(this); + Container p = getParent(); + if (p instanceof JLayeredPane) + ((JLayeredPane) p).moveToBack(this); } /** @@ -1172,8 +1146,9 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void moveToFront() { - if (getParent() instanceof JLayeredPane) - ((JLayeredPane) getParent()).moveToFront(this); + Container p = getParent(); + if (p != null && p instanceof JLayeredPane) + ((JLayeredPane) p).moveToFront(this); } /** @@ -1196,6 +1171,7 @@ public class JInternalFrame extends JComponent implements Accessible, // Do nothing if they don't want to be restored first. } setSize(getPreferredSize()); + validate(); } /** @@ -1311,7 +1287,6 @@ public class JInternalFrame extends JComponent implements Accessible, dispose(); firePropertyChange(IS_CLOSED_PROPERTY, false, true); - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSED); } } @@ -1468,7 +1443,9 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setJMenuBar(JMenuBar b) { + JMenuBar old = getJMenuBar(); getRootPane().setJMenuBar(b); + firePropertyChange(MENU_BAR_PROPERTY, old, b); } /** @@ -1493,11 +1470,17 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setLayer(Integer layer) { - JDesktopPane p = getDesktopPane(); - if (p != null) + Container p = getParent(); + if (p instanceof JLayeredPane) { - int pos = p.getPosition(this); - p.setLayer(this, layer.intValue(), pos); + JLayeredPane lp = (JLayeredPane) p; + lp.setLayer(this, layer.intValue(), lp.getPosition(this)); + } + else + { + JLayeredPane.putLayer(this, layer.intValue()); + if (p != null) + p.repaint(getX(), getY(), getWidth(), getHeight()); } } @@ -1508,6 +1491,9 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setLayeredPane(JLayeredPane layered) { + if (layered == null) + throw new IllegalComponentStateException("LayeredPane must not be null"); + if (layered != getLayeredPane()) { JLayeredPane old = getLayeredPane(); @@ -1561,15 +1547,11 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setMaximum(boolean b) throws PropertyVetoException { - if (b != isMaximum()) + if (b != isMaximum) { - fireVetoableChange(IS_MAXIMUM_PROPERTY, b, isMaximum); + fireVetoableChange(IS_MAXIMUM_PROPERTY, isMaximum, b); isMaximum = b; - if (b) - setNormalBounds(getBounds()); - maxTransition = ! b; firePropertyChange(IS_MAXIMUM_PROPERTY, ! isMaximum, isMaximum); - maxTransition = false; } } @@ -1593,7 +1575,7 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setNormalBounds(Rectangle r) { - storedBounds.setBounds(r.x, r.y, r.width, r.height); + storedBounds = r; } /** @@ -1621,8 +1603,23 @@ public class JInternalFrame extends JComponent implements Accessible, if (rootPane != null) remove(rootPane); + JRootPane old = rootPane; rootPane = root; - add(root); + + if (rootPane != null) + { + boolean checkingEnabled = isRootPaneCheckingEnabled(); + try + { + setRootPaneCheckingEnabled(false); + add(rootPane, BorderLayout.CENTER); + } + finally + { + setRootPaneCheckingEnabled(checkingEnabled); + } + } + firePropertyChange(ROOT_PANE_PROPERTY, old, rootPane); } /** @@ -1652,27 +1649,26 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setSelected(boolean selected) throws PropertyVetoException { - if (selected != isSelected()) + if (selected != isSelected + && (! selected || (isIcon ? desktopIcon.isShowing() : isShowing()))) { - fireVetoableChange(IS_SELECTED_PROPERTY, selected, isSelected); - - if (! selected) - defaultFocus = getMostRecentFocusOwner(); + fireVetoableChange(IS_SELECTED_PROPERTY, isSelected, selected); - isSelected = selected; + if (! selected) + defaultFocus = getMostRecentFocusOwner(); - if (selected) - restoreSubcomponentFocus(); + isSelected = selected; + firePropertyChange(IS_SELECTED_PROPERTY, ! isSelected, isSelected); - if (isShowing()) - repaint(); + if (isSelected) + fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_ACTIVATED); + else + fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_DEACTIVATED); - firePropertyChange(IS_SELECTED_PROPERTY, ! isSelected, isSelected); + if (selected) + restoreSubcomponentFocus(); - if (isSelected) - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_ACTIVATED); - else - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_DEACTIVATED); + repaint(); } } @@ -1687,14 +1683,9 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setTitle(String title) { - if (title == null && this.title == null) - return; - if (title == null || this.title == null || ! this.title.equals(title)) - { - String old = this.title; - this.title = title; - firePropertyChange(TITLE_PROPERTY, old, this.title); - } + String old = this.title; + this.title = title; + firePropertyChange(TITLE_PROPERTY, old, this.title); } /** @@ -1707,12 +1698,21 @@ public class JInternalFrame extends JComponent implements Accessible, { if (! isVisible()) { + if (isFirstTimeVisible) + { + isFirstTimeVisible = false; + fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_OPENED); + } + + getDesktopIcon().setVisible(true); + + toFront(); super.show(); - JDesktopPane pane = getDesktopPane(); - if (pane != null) - pane.setSelectedFrame(this); - else + if (isIcon()) + return; + + if (! isSelected()) { try { @@ -1723,11 +1723,6 @@ public class JInternalFrame extends JComponent implements Accessible, // Do nothing. if they don't want to be selected. } } - if (isFirstTimeVisible) - { - isFirstTimeVisible = false; - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_OPENED); - } } } diff --git a/libjava/classpath/javax/swing/JLabel.java b/libjava/classpath/javax/swing/JLabel.java index a5fe3ba1af0..fcf0fd7cb13 100644 --- a/libjava/classpath/javax/swing/JLabel.java +++ b/libjava/classpath/javax/swing/JLabel.java @@ -46,6 +46,7 @@ import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; @@ -57,7 +58,7 @@ import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; /** - * A swing widget that displays a text message and/or an icon. + * A component that displays a static text message and/or an icon. */ public class JLabel extends JComponent implements Accessible, SwingConstants { @@ -359,7 +360,7 @@ public class JLabel extends JComponent implements Accessible, SwingConstants /** The label's mnemnonic key. */ private transient int displayedMnemonic = KeyEvent.VK_UNDEFINED; - /** The index of the menemonic character in the text. */ + /** The index of the mnemonic character in the text. */ private transient int displayedMnemonicIndex = -1; /** The gap between the icon and the text. */ @@ -435,11 +436,12 @@ public class JLabel extends JComponent implements Accessible, SwingConstants this.icon = icon; this.horizontalAlignment = horizontalAlignment; setAlignmentX(0.0F); + setInheritsPopupMenu(true); updateUI(); } /** - * This method returns the label's UI delegate. + * Returns the label's UI delegate. * * @return The label's UI delegate. */ @@ -449,9 +451,9 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method sets the label's UI delegate. + * Sets the label's UI delegate. * - * @param ui The label's UI delegate. + * @param ui The label's UI delegate (<code>null</code> not permitted). */ public void setUI(LabelUI ui) { @@ -459,8 +461,8 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method resets the label's UI delegate to the default UI for the - * current look and feel. + * Resets the label's UI delegate to the default UI for the current look and + * feel. */ public void updateUI() { @@ -468,10 +470,10 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns a name to identify which look and feel class will be + * Returns a name to identify which look and feel class will be * the UI delegate for this label. * - * @return The UIClass identifier. "LabelUI" + * @return <code>"LabelUI"</code> */ public String getUIClassID() { @@ -518,9 +520,11 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the label text. + * Returns the text displayed by the label. * - * @return The label text. + * @return The label text (possibly <code>null</code>). + * + * @see #setText(String) */ public String getText() { @@ -528,31 +532,42 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "text" property. The given text will be painted - * in the label. + * Sets the text for the label and sends a {@link PropertyChangeEvent} (with + * the name 'text') to all registered listeners. This method will also + * update the <code>displayedMnemonicIndex</code>, if necessary. * - * @param newText The label's text. + * @param newText The text (<code>null</code> permitted). + * + * @see #getText() + * @see #getDisplayedMnemonicIndex() */ public void setText(String newText) { - if (text != newText) - { - String oldText = text; - text = newText; - firePropertyChange("text", oldText, newText); + if (text == null && newText == null) + return; + if (text != null && text.equals(newText)) + return; - if (text != null && text.length() <= displayedMnemonicIndex) - setDisplayedMnemonicIndex(text.length() - 1); - revalidate(); - repaint(); - } + String oldText = text; + text = newText; + firePropertyChange("text", oldText, newText); + + if (text != null) + setDisplayedMnemonicIndex(text.toUpperCase().indexOf(displayedMnemonic)); + else + setDisplayedMnemonicIndex(-1); + revalidate(); + repaint(); } /** - * This method returns the active icon. The active icon is painted when the - * label is enabled. + * Returns the active icon. The active icon is painted when the label is + * enabled. * * @return The active icon. + * + * @see #setIcon(Icon) + * @see #getDisabledIcon() */ public Icon getIcon() { @@ -560,81 +575,93 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "icon" property. This icon (the active icon) will - * be the one displayed when the label is enabled. + * Sets the icon for the label (this is a bound property with the name + * 'icon'). This icon will be displayed when the label is enabled. * - * @param newIcon The active icon. + * @param newIcon The icon (<code>null</code> permitted). + * + * @see #getIcon() + * @see #setDisabledIcon(Icon) */ public void setIcon(Icon newIcon) { if (icon != newIcon) { - Icon oldIcon = icon; - icon = newIcon; - firePropertyChange("icon", oldIcon, newIcon); - repaint(); + Icon oldIcon = icon; + icon = newIcon; + firePropertyChange("icon", oldIcon, newIcon); + repaint(); } } /** - * This method returns the disabled icon. The disabled icon is painted when - * the label is disabled. If the disabled icon is null and the active icon - * is an ImageIcon, this method returns a grayed version of the icon. The - * grayed version of the icon becomes the disabledIcon. + * Returns the disabled icon. The disabled icon is painted when the label is + * disabled. If the disabled icon is <code>null</code> and the active icon + * is an {@link ImageIcon}, this method returns a grayed version of the icon. + * The grayed version of the icon becomes the <code>disabledIcon</code>. * * @return The disabled icon. + * + * @see #setDisabledIcon(Icon) */ public Icon getDisabledIcon() { if (disabledIcon == null && icon instanceof ImageIcon) - disabledIcon = new ImageIcon(GrayFilter.createDisabledImage(((ImageIcon) icon) - .getImage())); + disabledIcon = new ImageIcon( + GrayFilter.createDisabledImage(((ImageIcon) icon).getImage())); return disabledIcon; } /** - * This method changes the "disabledIcon" property. This icon (the disabled - * icon) will be the one displayed when the label is disabled. + * Sets the icon displayed when the label is disabled (this is a bound + * property with the name 'disabledIcon'). * - * @param newIcon The disabled icon. + * @param newIcon The disabled icon (<code>null</code> permitted). + * + * @see #getDisabledIcon() */ public void setDisabledIcon(Icon newIcon) { if (disabledIcon != newIcon) { - Icon oldIcon = disabledIcon; - disabledIcon = newIcon; - firePropertyChange("disabledIcon", oldIcon, newIcon); + Icon oldIcon = disabledIcon; + disabledIcon = newIcon; + firePropertyChange("disabledIcon", oldIcon, newIcon); } } /** - * This method sets the keycode that will be the label's mnemonic. If the - * label is used as a label for another component, the label will give - * focus to that component when the mnemonic is activated. + * Sets the keycode that will be the label's mnemonic (this is a bound + * property with the name 'displayedMnemonic'). If the label is used as a + * label for another component, the label will give focus to that component + * when the mnemonic is activated. * * @param mnemonic The keycode to use for the mnemonic. + * + * @see #getDisplayedMnemonic() */ public void setDisplayedMnemonic(int mnemonic) { if (displayedMnemonic != mnemonic) { - firePropertyChange("displayedMnemonic", - displayedMnemonic, mnemonic); - displayedMnemonic = mnemonic; - - if (text != null) - setDisplayedMnemonicIndex(text.toUpperCase().indexOf(mnemonic)); + int old = displayedMnemonic; + displayedMnemonic = mnemonic; + firePropertyChange("displayedMnemonic", old, displayedMnemonic); + if (text != null) + setDisplayedMnemonicIndex(text.toUpperCase().indexOf(mnemonic)); } } /** - * This method sets the character that will be the mnemonic used. If the + * Sets the character that will be the label's mnemonic. If the * label is used as a label for another component, the label will give - * focus to that component when the mnemonic is activated. + * focus to that component when the mnemonic is activated via the keyboard. * - * @param mnemonic The character to use for the mnemonic. + * @param mnemonic The character to use for the mnemonic (this will be + * converted to the equivalent upper case character). + * + * @see #getDisplayedMnemonic() */ public void setDisplayedMnemonic(char mnemonic) { @@ -642,51 +669,63 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the keycode that is used for the label's mnemonic. + * Returns the keycode that is used for the label's mnemonic. * * @return The keycode that is used for the label's mnemonic. + * + * @see #setDisplayedMnemonic(int) */ public int getDisplayedMnemonic() { - return (int) displayedMnemonic; + return displayedMnemonic; } /** - * This method sets which character in the text will be the underlined - * character. If the given index is -1, then this indicates that there is - * no mnemonic. If the index is less than -1 or if the index is equal to - * the length, this method will throw an IllegalArgumentException. + * Sets the index of the character in the text that will be underlined to + * indicate that it is the mnemonic character for the label. You only need + * to call this method if you wish to override the automatically calculated + * character index. For instance, for a label "Find Next" with the mnemonic + * character 'n', you might wish to underline the second occurrence of 'n' + * rather than the first (which is the default). + * <br><br> + * Note that this method does not validate the character at the specified + * index to ensure that it matches the key code returned by + * {@link #getDisplayedMnemonic()}. * * @param newIndex The index of the character to underline. * - * @throws IllegalArgumentException If index less than -1 or index equals - * length. + * @throws IllegalArgumentException If index less than -1 or index is greater + * than or equal to the label length. + * + * @see #getDisplayedMnemonicIndex() + * @since 1.4 */ public void setDisplayedMnemonicIndex(int newIndex) throws IllegalArgumentException { - if (newIndex < -1 || (text != null && newIndex >= text.length())) + int maxValid = -1; + if (text != null) + maxValid = text.length() - 1; + if (newIndex < -1 || newIndex > maxValid) throw new IllegalArgumentException(); - if (newIndex == -1 - || text == null - || text.charAt(newIndex) != displayedMnemonic) - newIndex = -1; - if (newIndex != displayedMnemonicIndex) { - int oldIndex = displayedMnemonicIndex; - displayedMnemonicIndex = newIndex; - firePropertyChange("displayedMnemonicIndex", - oldIndex, newIndex); + int oldIndex = displayedMnemonicIndex; + displayedMnemonicIndex = newIndex; + firePropertyChange("displayedMnemonicIndex", oldIndex, newIndex); } } /** - * This method returns which character in the text will be the underlined - * character. + * Returns the index of the character in the label's text that will be + * underlined (to indicate that it is the mnemonic character), or -1 if no + * character is to be underlined. * * @return The index of the character that will be underlined. + * + * @see #setDisplayedMnemonicIndex(int) + * @since 1.4 */ public int getDisplayedMnemonicIndex() { @@ -694,14 +733,16 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method ensures that the key is valid as a horizontal alignment. - * Valid keys are: LEFT, CENTER, RIGHT, LEADING, TRAILING + * Checks the specified key to ensure that it is valid as a horizontal + * alignment, throwing an {@link IllegalArgumentException} if the key is + * invalid. Valid keys are {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, + * {@link #LEADING} and {@link #TRAILING}. * * @param key The key to check. * @param message The message of the exception to be thrown if the key is * invalid. * - * @return The key if it's valid. + * @return The key if it is valid. * * @throws IllegalArgumentException If the key is invalid. */ @@ -715,14 +756,15 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method ensures that the key is valid as a vertical alignment. Valid - * keys are: TOP, CENTER, and BOTTOM. + * Checks the specified key to ensure that it is valid as a vertical + * alignment, throwing an {@link IllegalArgumentException} if the key is + * invalid. Valid keys are {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}. * * @param key The key to check. * @param message The message of the exception to be thrown if the key is * invalid. * - * @return The key if it's valid. + * @return The key if it is valid. * * @throws IllegalArgumentException If the key is invalid. */ @@ -735,9 +777,11 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the gap between the icon and the text. + * Returns the gap between the icon and the text. * * @return The gap between the icon and the text. + * + * @see #setIconTextGap(int) */ public int getIconTextGap() { @@ -745,10 +789,12 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "iconTextGap" property. The iconTextGap - * determines how much space there is between the icon and the text. + * Sets the gap between the icon and the text, in the case that both are + * visible (this is a bound property with the name 'iconTextGap'). * - * @param newGap The gap between the icon and the text. + * @param newGap The gap (in pixels). + * + * @see #getIconTextGap() */ public void setIconTextGap(int newGap) { @@ -760,9 +806,13 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the vertical alignment of the label. + * Returns the vertical alignment of the label (one of + * {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}). The default value + * depends on the installed look and feel, but is usually {@link #CENTER}. * - * @return The vertical alignment of the label. + * @return The vertical alignment. + * + * @see #setVerticalAlignment(int) */ public int getVerticalAlignment() { @@ -770,12 +820,18 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "verticalAlignment" property of the label. The - * vertical alignment determines how where the label will be placed - * vertically. If the alignment is not valid, it will default to the - * center. + * Sets the vertical alignment for the label (this is a bound property with + * the name 'verticalAlignment'). The vertical alignment determines where + * the label (icon and text) will be placed vertically within the component + * bounds. Valid alignment codes are {@link #TOP}, {@link #CENTER} and + * {@link #BOTTOM}. * * @param alignment The vertical alignment of the label. + * + * @throws IllegalArgumentException if <code>alignment</code> is not one of + * the specified values. + * + * @see #getVerticalAlignment() */ public void setVerticalAlignment(int alignment) { @@ -788,9 +844,14 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the horziontal alignment of the label. + * Returns the horizontal alignment of the label (one of {@link #LEFT}, + * {@link #CENTER}, {@link #RIGHT}, {@link #LEADING} and {@link #TRAILING}). + * The default value depends on the installed look and feel, but is usually + * {@link #LEFT}. * - * @return The horizontal alignment of the label. + * @return The horizontal alignment. + * + * @see #setHorizontalAlignment(int) */ public int getHorizontalAlignment() { @@ -798,10 +859,18 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "horizontalAlignment" property. The horizontal - * alignment determines where the label will be placed horizontally. + * Sets the horizontal alignment for the label (this is a bound property with + * the name 'horizontalAlignment'). The horizontal alignment determines where + * the label (icon and text) will be placed horizontally within the + * component bounds. Valid alignment codes are {@link #LEFT}, + * {@link #CENTER}, {@link #RIGHT}, {@link #LEADING} and {@link #TRAILING}. * * @param alignment The horizontal alignment of the label. + * + * @throws IllegalArgumentException if <code>alignment</code> is not one of + * the specified values. + * + * @see #getHorizontalAlignment() */ public void setHorizontalAlignment(int alignment) { @@ -815,9 +884,12 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the vertical text position of the label. - * - * @return The vertical text position of the label. + * Returns the vertical position of the label's text relative to the icon. + * This will be one of {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}. + * + * @return The vertical position of the label's text relative to the icon. + * + * @see #setVerticalTextPosition(int) */ public int getVerticalTextPosition() { @@ -825,28 +897,35 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "verticalTextPosition" property of the label. The - * vertical text position determines where the text will be placed - * vertically relative to the icon. + * Sets the vertical position of the label's text relative to the icon (this + * is a bound property with the name 'verticalTextPosition'). Valid + * positions are {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}. * * @param textPosition The vertical text position. + * + * @throws IllegalArgumentException if <code>textPosition</code> is not one + * of the specified values. */ public void setVerticalTextPosition(int textPosition) { if (textPosition != verticalTextPosition) { - int oldPos = verticalTextPosition; - verticalTextPosition = checkVerticalKey(textPosition, - "verticalTextPosition"); - firePropertyChange("verticalTextPosition", oldPos, - verticalTextPosition); + int oldPos = verticalTextPosition; + verticalTextPosition = checkVerticalKey(textPosition, + "verticalTextPosition"); + firePropertyChange("verticalTextPosition", oldPos, + verticalTextPosition); } } /** - * This method returns the horizontal text position of the label. - * - * @return The horizontal text position. + * Returns the horizontal position of the label's text relative to the icon. + * This will be one of {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, + * {@link #LEADING} and {@link #TRAILING}. + * + * @return The horizontal position of the label's text relative to the icon. + * + * @see #setHorizontalTextPosition(int) */ public int getHorizontalTextPosition() { @@ -854,28 +933,31 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "horizontalTextPosition" property of the label. - * The horizontal text position determines where the text will be placed - * horizontally relative to the icon. + * Sets the horizontal position of the label's text relative to the icon (this + * is a bound property with the name 'horizontalTextPosition'). Valid + * positions are {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, + * {@link #LEADING} and {@link #TRAILING}. * * @param textPosition The horizontal text position. + * + * @throws IllegalArgumentException if <code>textPosition</code> is not one + * of the specified values. */ public void setHorizontalTextPosition(int textPosition) { if (textPosition != horizontalTextPosition) { - int oldPos = horizontalTextPosition; - horizontalTextPosition = checkHorizontalKey(textPosition, - "horizontalTextPosition"); - firePropertyChange("horizontalTextPosition", oldPos, - horizontalTextPosition); + int oldPos = horizontalTextPosition; + horizontalTextPosition = checkHorizontalKey(textPosition, + "horizontalTextPosition"); + firePropertyChange("horizontalTextPosition", oldPos, + horizontalTextPosition); } } /** - * This method simply returns false if the current icon image (current icon - * will depend on whether the label is enabled) is not equal to the passed - * in image. + * Returns false if the current icon image (current icon will depend on + * whether the label is enabled) is not equal to the passed in image. * * @param img The image to check. * @param infoflags The bitwise inclusive OR of ABORT, ALLBITS, ERROR, @@ -900,11 +982,11 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the component that the label gives focus to when the - * mnemonic is activated. + * Returns the component that this <code>JLabel</code> is providing the label + * for. This component will typically receive the focus when the label's + * mnemonic key is activated via the keyboard. * - * @return The component that gets focus when the label's mnemonic is - * activated. + * @return The component (possibly <code>null</code>). */ public Component getLabelFor() { @@ -912,12 +994,14 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "labelFor" property. The component that the label - * is acting as a label for will request focus when the label's mnemonic - * is activated. + * Sets the component that this <code>JLabel</code> is providing the label + * for (this is a bound property with the name 'labelFor'). This component + * will typically receive the focus when the label's mnemonic key is + * activated via the keyboard. * - * @param c The component that gets focus when the label's mnemonic is - * activated. + * @param c the component (<code>null</code> permitted). + * + * @see #getLabelFor() */ public void setLabelFor(Component c) { @@ -946,10 +1030,9 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method overrides setFont so that we can call for a repaint after the - * font is changed. + * Sets the font for the label (this a bound property with the name 'font'). * - * @param f The font for this label. + * @param f The font (<code>null</code> permitted). */ public void setFont(Font f) { diff --git a/libjava/classpath/javax/swing/JLayeredPane.java b/libjava/classpath/javax/swing/JLayeredPane.java index 11650e721d0..ca913e97fed 100644 --- a/libjava/classpath/javax/swing/JLayeredPane.java +++ b/libjava/classpath/javax/swing/JLayeredPane.java @@ -43,6 +43,7 @@ import java.awt.Component; import java.awt.Container; import java.awt.Graphics; import java.awt.Rectangle; +import java.util.ArrayList; import java.util.Hashtable; import javax.accessibility.Accessible; @@ -326,13 +327,13 @@ public class JLayeredPane extends JComponent implements Accessible { int pos = -1; int index = getIndexOf(c); - Component[] components = getComponents(); - int layer = getLayer(c); if (index >= 0) { - for (int i = index; i >= 0; --i) + pos = 0; + int layer = getLayer(c); + for (int i = index - 1; i >= 0; --i) { - if (layer == getLayer(components[i])) + if (layer == getLayer(getComponent(i))) pos++; else break; @@ -353,9 +354,7 @@ public class JLayeredPane extends JComponent implements Accessible */ public void setPosition(Component c, int position) { - int layer = getLayer(c); - int index = insertIndexForLayer(layer, position); - setComponentZOrder(c, index); + setLayer(c, getLayer(c), position); } /** @@ -478,34 +477,85 @@ public class JLayeredPane extends JComponent implements Accessible */ protected int insertIndexForLayer(int layer, int position) { - // position < 0 means insert at greatest position within layer. - if (position < 0) - position = Integer.MAX_VALUE; + return insertIndexForLayer(null, layer, position); + } - Component[] components = getComponents(); - int index = 0; + /** + * Similar to {@link #insertIndexForLayer(int, int)}, only that it takes a + * component parameter, which should be ignored in the search. This is + * necessary to support {@link #setLayer(Component, int, int)} which uses + * Container.setComponentZOrder(), which doesn't remove the component. + * + * @param comp the component to ignore + * @param layer the layer + * @param position the position + * + * @return the insertion index + */ + private int insertIndexForLayer(Component comp, int layer, int position) + { + // Create the component list to search through. + ArrayList l = new ArrayList(); + int count = getComponentCount(); + for (int i = 0; i < count; i++) + { + Component c = getComponent(i); + if (c != comp) + l.add(c); + } - // Try to find the start index of the specified layer. - int p = -1; - for (int i = 0; i < components.length; i++) + count = l.size(); + int layerStart = -1; + int layerEnd = -1; + for (int i = 0; i < count; i++) { - int l = getLayer(components[i]); - if (l > layer) - index++; - // If we are in the layer we look for, try to find the position. - else if (l == layer) + int layerOfComponent = getLayer((Component) l.get(i)); + if (layerStart == -1 && layerOfComponent == layer) + layerStart = i; + if (layerOfComponent < layer) { - p++; - if (p < position) - index++; + // We are beyond the layer that we are looking for. Update the + // layerStart and layerEnd and exit the loop. + if (i == 0) + { + layerStart = 0; + layerEnd = 0; + } else - break; + layerEnd = i; + break; } - // No need to look further if the layer at i is smaller than layer. + } + + // No layer found. The requested layer is lower than any existing layer, + // put the component at the end. + int insertIndex; + if (layerStart == -1 && layerEnd == -1) + { + insertIndex = count; + } + else + { + // Corner cases. + if (layerStart != -1 && layerEnd == -1) + layerEnd = count; + if (layerStart == -1 && layerEnd != -1) + layerStart = layerEnd; + + // Adding to the bottom of a layer returns the end index + // in the layer. + if (position == -1) + insertIndex = layerEnd; else - break; + { + // Insert into a layer. + if (position > -1 && layerStart + position <= layerEnd) + insertIndex = layerStart + position; + else + insertIndex = layerEnd; + } } - return index; + return insertIndex; } /** @@ -559,17 +609,29 @@ public class JLayeredPane extends JComponent implements Accessible public void setLayer(Component c, int layer, int position) { Integer layerObj = getObjectForLayer(layer); - if (c instanceof JComponent) + + // Nothing to do if neither the layer nor the position is + // changed. + if (layer != getLayer(c) || position != getPosition(c)) { - JComponent jc = (JComponent) c; - jc.putClientProperty(LAYER_PROPERTY, layerObj); - } - else - componentToLayer.put (c, layerObj); + // Store the layer either in the JComponent or in the hashtable + if (c instanceof JComponent) + { + JComponent jc = (JComponent) c; + jc.putClientProperty(LAYER_PROPERTY, layerObj); + } + else + componentToLayer.put (c, layerObj); - // Set position only of component is already added to this layered pane. - if (getIndexOf(c) != -1) - setPosition(c, position); + // Update the component in the Z order of the Container. + Container parent = c.getParent(); + if (parent == this) + { + int index = insertIndexForLayer(c, layer, position); + setComponentZOrder(c, index); + } + } + repaint(c.getX(), c.getY(), c.getWidth(), c.getHeight()); } /** @@ -592,14 +654,17 @@ public class JLayeredPane extends JComponent implements Accessible { int layer; if (layerConstraint != null && layerConstraint instanceof Integer) - layer = ((Integer) layerConstraint).intValue(); + { + layer = ((Integer) layerConstraint).intValue(); + setLayer(comp, layer); + } else - layer = getLayer(comp); + layer = getLayer(comp); int newIdx = insertIndexForLayer(layer, index); - setLayer(comp, layer); super.addImpl(comp, layerConstraint, newIdx); - repaint(comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight()); + comp.validate(); + comp.repaint(); } /** diff --git a/libjava/classpath/javax/swing/JList.java b/libjava/classpath/javax/swing/JList.java index eab6be3c30c..6a98770eeda 100644 --- a/libjava/classpath/javax/swing/JList.java +++ b/libjava/classpath/javax/swing/JList.java @@ -944,17 +944,6 @@ public class JList extends JComponent implements Accessible, Scrollable */ ListSelectionModel selectionModel; - - /** - * This property indicates that the list's selection is currently - * "adjusting" -- perhaps due to a user actively dragging the mouse over - * multiple list elements -- and is therefore likely to change again in - * the near future. A {@link ListSelectionListener} might choose to delay - * updating its view of the list's selection until this property is - * false, meaning that the adjustment has completed. - */ - boolean valueIsAdjusting; - /** * This property indicates a <em>preference</em> for the number of rows * displayed in the list, and will scale the @@ -1085,8 +1074,7 @@ public class JList extends JComponent implements Accessible, Scrollable fixedCellWidth = -1; layoutOrientation = VERTICAL; opaque = true; - valueIsAdjusting = false; - visibleRowCount = 7; + visibleRowCount = 8; cellRenderer = new DefaultListCellRenderer(); listListener = new ListListener(); @@ -1196,11 +1184,13 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Gets the value of the {@link #visibleRowCount} property. + * Gets the value of the {@link #visibleRowCount} property. The default + * value is 8. * * @return the current value of the property. + * + * @see #setVisibleRowCount(int) */ - public int getVisibleRowCount() { return visibleRowCount; @@ -1210,12 +1200,19 @@ public class JList extends JComponent implements Accessible, Scrollable * Sets the value of the {@link #visibleRowCount} property. * * @param vc The new property value + * + * @see #getVisibleRowCount() */ public void setVisibleRowCount(int vc) { - visibleRowCount = vc; - revalidate(); - repaint(); + if (visibleRowCount != vc) + { + int oldValue = visibleRowCount; + visibleRowCount = Math.max(vc, 0); + firePropertyChange("visibleRowCount", oldValue, vc); + revalidate(); + repaint(); + } } /** @@ -2184,23 +2181,25 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Returns the value of the <code>valueIsAdjusting</code> property. + * Returns the <code>valueIsAdjusting</code> flag from the list's selection + * model. * * @return the value */ public boolean getValueIsAdjusting() { - return valueIsAdjusting; + return selectionModel.getValueIsAdjusting(); } /** - * Sets the <code>valueIsAdjusting</code> property. + * Sets the <code>valueIsAdjusting</code> flag in the list's selection + * model. * * @param isAdjusting the new value */ public void setValueIsAdjusting(boolean isAdjusting) { - valueIsAdjusting = isAdjusting; + selectionModel.setValueIsAdjusting(isAdjusting); } /** @@ -2228,11 +2227,13 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Returns the layout orientation. + * Returns the layout orientation, which will be one of {@link #VERTICAL}, + * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. The default value + * is {@link #VERTICAL}. * - * @return the orientation, one of <code>JList.VERTICAL</code>, - * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code> + * @return the orientation. * + * @see #setLayoutOrientation(int) * @since 1.4 */ public int getLayoutOrientation() @@ -2241,15 +2242,21 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Sets the layout orientation. + * Sets the layout orientation (this is a bound property with the name + * 'layoutOrientation'). Valid orientations are {@link #VERTICAL}, + * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. * - * @param orientation the orientation to set, one of <code>JList.VERTICAL</code>, - * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code> + * @param orientation the orientation. * + * @throws IllegalArgumentException if <code>orientation</code> is not one + * of the specified values. * @since 1.4 + * @see #getLayoutOrientation() */ public void setLayoutOrientation(int orientation) { + if (orientation < JList.VERTICAL || orientation > JList.HORIZONTAL_WRAP) + throw new IllegalArgumentException(); if (layoutOrientation == orientation) return; @@ -2282,14 +2289,16 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Returns the next list element (beginning from <code>startIndex</code> - * that starts with <code>prefix</code>. Searching is done in the direction - * specified by <code>bias</code>. + * Returns the index of the next list element (beginning at + * <code>startIndex</code> and moving in the specified direction through the + * list, looping around if necessary) that starts with <code>prefix</code> + * (ignoring case). * * @param prefix the prefix to search for in the cell values * @param startIndex the index where to start searching from - * @param bias the search direction, either {@link Position.Bias#Forward} - * or {@link Position.Bias#Backward} + * @param direction the search direction, either {@link Position.Bias#Forward} + * or {@link Position.Bias#Backward} (<code>null</code> is interpreted + * as {@link Position.Bias#Backward}. * * @return the index of the found element or -1 if no such element has * been found @@ -2299,7 +2308,8 @@ public class JList extends JComponent implements Accessible, Scrollable * * @since 1.4 */ - public int getNextMatch(String prefix, int startIndex, Position.Bias bias) + public int getNextMatch(String prefix, int startIndex, + Position.Bias direction) { if (prefix == null) throw new IllegalArgumentException("The argument 'prefix' must not be" @@ -2309,37 +2319,33 @@ public class JList extends JComponent implements Accessible, Scrollable + " be less than zero."); int size = model.getSize(); - if (startIndex > model.getSize()) + if (startIndex >= model.getSize()) throw new IllegalArgumentException("The argument 'startIndex' must not" + " be greater than the number of" + " elements in the ListModel."); - int index = -1; - if (bias == Position.Bias.Forward) - { - for (int i = startIndex; i < size; i++) - { - String item = model.getElementAt(i).toString(); - if (item.startsWith(prefix)) - { - index = i; - break; - } - } - } - else + int result = -1; + int current = startIndex; + int delta = -1; + int itemCount = model.getSize(); + boolean finished = false; + prefix = prefix.toUpperCase(); + + if (direction == Position.Bias.Forward) + delta = 1; + while (!finished) { - for (int i = startIndex; i >= 0; i--) - { - String item = model.getElementAt(i).toString(); - if (item.startsWith(prefix)) - { - index = i; - break; - } - } + String itemStr = model.getElementAt(current).toString().toUpperCase(); + if (itemStr.startsWith(prefix)) + return current; + current = (current + delta); + if (current == -1) + current += itemCount; + else + current = current % itemCount; + finished = current == startIndex; } - return index; + return result; } /** diff --git a/libjava/classpath/javax/swing/JMenu.java b/libjava/classpath/javax/swing/JMenu.java index 02cb20eab57..0840509f906 100644 --- a/libjava/classpath/javax/swing/JMenu.java +++ b/libjava/classpath/javax/swing/JMenu.java @@ -40,6 +40,7 @@ package javax.swing; import java.awt.Component; import java.awt.Point; +import java.awt.PopupMenu; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; @@ -74,7 +75,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement private static final long serialVersionUID = 4227225638931828014L; /** A Popup menu associated with this menu, which pops up when menu is selected */ - private JPopupMenu popupMenu = new JPopupMenu(); + private JPopupMenu popupMenu = null; /** Whenever menu is selected or deselected the MenuEvent is fired to menu's registered listeners. */ @@ -98,6 +99,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement { super(); setOpaque(false); + setDelay(200); } /** @@ -108,8 +110,10 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement public JMenu(String text) { super(text); + popupMenu = new JPopupMenu(); popupMenu.setInvoker(this); setOpaque(false); + setDelay(200); } /** @@ -122,8 +126,10 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement { super(action); createActionChangeListener(this); + popupMenu = new JPopupMenu(); popupMenu.setInvoker(this); setOpaque(false); + setDelay(200); } /** @@ -137,6 +143,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement { // FIXME: tearoff not implemented this(text); + setDelay(200); } /** @@ -148,7 +155,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public JMenuItem add(JMenuItem item) { - return popupMenu.add(item); + return getPopupMenu().add(item); } /** @@ -160,7 +167,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public Component add(Component component) { - popupMenu.insert(component, -1); + getPopupMenu().insert(component, -1); return component; } @@ -174,7 +181,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public Component add(Component component, int index) { - return popupMenu.add(component, index); + return getPopupMenu().add(component, index); } /** @@ -186,7 +193,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public JMenuItem add(String text) { - return popupMenu.add(text); + return getPopupMenu().add(text); } /** @@ -198,7 +205,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public JMenuItem add(Action action) { - return popupMenu.add(action); + return getPopupMenu().add(action); } /** @@ -209,7 +216,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public void remove(JMenuItem item) { - popupMenu.remove(item); + getPopupMenu().remove(item); } /** @@ -219,7 +226,11 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public void remove(int index) { - popupMenu.remove(index); + if (index < 0 || (index > 0 && getMenuComponentCount() == 0)) + throw new IllegalArgumentException(); + + if (getMenuComponentCount() > 0) + popupMenu.remove(index); } /** @@ -229,8 +240,9 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public void remove(Component component) { - int index = popupMenu.getComponentIndex(component); - popupMenu.remove(index); + int index = getPopupMenu().getComponentIndex(component); + if (index >= 0) + getPopupMenu().remove(index); } /** @@ -238,7 +250,8 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public void removeAll() { - popupMenu.removeAll(); + if (popupMenu != null) + popupMenu.removeAll(); } /** @@ -267,7 +280,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement if (index < 0) throw new IllegalArgumentException("index less than zero"); - popupMenu.insert(item, index); + getPopupMenu().insert(item, index); return item; } @@ -381,7 +394,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement super.setSelected(false); super.setArmed(false); fireMenuDeselected(); - popupMenu.setVisible(false); + getPopupMenu().setVisible(false); } } @@ -404,7 +417,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public boolean isPopupMenuVisible() { - return popupMenu.isVisible(); + return getPopupMenu().isVisible(); } /** @@ -415,7 +428,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement public void setPopupMenuVisible(boolean popup) { if (getModel().isEnabled()) - popupMenu.setVisible(popup); + getPopupMenu().setVisible(popup); } /** @@ -530,6 +543,9 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement if (index < 0) throw new IllegalArgumentException("index less than 0"); + if (getItemCount() == 0) + return null; + Component c = popupMenu.getComponentAtIndex(index); if (c instanceof JMenuItem) @@ -558,7 +574,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement public boolean isTearOff() { // NOT YET IMPLEMENTED - return false; + throw new Error("The method isTearOff() has not yet been implemented."); } /** @@ -568,7 +584,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public int getMenuComponentCount() { - return popupMenu.getComponentCount(); + return getPopupMenu().getComponentCount(); } /** @@ -581,6 +597,9 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public Component getMenuComponent(int index) { + if (getPopupMenu() == null || getMenuComponentCount() == 0) + return null; + return (Component) popupMenu.getComponentAtIndex(index); } @@ -591,7 +610,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public Component[] getMenuComponents() { - return popupMenu.getComponents(); + return getPopupMenu().getComponents(); } /** @@ -626,6 +645,11 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public JPopupMenu getPopupMenu() { + if (popupMenu == null) + { + popupMenu = new JPopupMenu(); + popupMenu.setInvoker(this); + } return popupMenu; } diff --git a/libjava/classpath/javax/swing/JMenuItem.java b/libjava/classpath/javax/swing/JMenuItem.java index 90d54b1d30a..f7f93bf00d7 100644 --- a/libjava/classpath/javax/swing/JMenuItem.java +++ b/libjava/classpath/javax/swing/JMenuItem.java @@ -184,7 +184,7 @@ public class JMenuItem extends AbstractButton implements Accessible, out statement below for now. */ //borderPainted = false; focusPainted = false; - horizontalAlignment = JButton.LEFT; + horizontalAlignment = JButton.LEADING; horizontalTextPosition = JButton.TRAILING; } diff --git a/libjava/classpath/javax/swing/JOptionPane.java b/libjava/classpath/javax/swing/JOptionPane.java index f9490553834..43caecd1a48 100644 --- a/libjava/classpath/javax/swing/JOptionPane.java +++ b/libjava/classpath/javax/swing/JOptionPane.java @@ -48,6 +48,8 @@ import java.awt.MenuComponent; import java.awt.Toolkit; import java.awt.event.MouseAdapter; import java.awt.event.MouseMotionAdapter; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; @@ -378,11 +380,49 @@ public class JOptionPane extends JComponent implements Accessible dialog.setResizable(false); dialog.pack(); dialog.setLocationRelativeTo(parentComponent); - + + addPropertyChangeListener(new ValuePropertyHandler(dialog)); return dialog; } /** + * Handles changes of the value property. Whenever this property changes, + * the JOptionPane dialog should be closed. + */ + private static class ValuePropertyHandler + implements PropertyChangeListener + { + /** + * The dialog to close. + */ + JDialog dialog; + + /** + * Creates a new instance. + * + * @param d the dialog to be closed + */ + ValuePropertyHandler(JDialog d) + { + dialog = d; + } + + /** + * Receives notification when any of the properties change. + */ + public void propertyChange(PropertyChangeEvent p) + { + String prop = p.getPropertyName(); + Object val = p.getNewValue(); + if (prop.equals(VALUE_PROPERTY) && val != null + && val != UNINITIALIZED_VALUE) + { + dialog.setVisible(false); + } + } + } + + /** * This method creates a new JInternalFrame that is in the JLayeredPane * which contains the parentComponent given. If no suitable JLayeredPane * can be found from the parentComponent given, a RuntimeException will be diff --git a/libjava/classpath/javax/swing/JPopupMenu.java b/libjava/classpath/javax/swing/JPopupMenu.java index c7890ea0e1e..d46015afdf3 100644 --- a/libjava/classpath/javax/swing/JPopupMenu.java +++ b/libjava/classpath/javax/swing/JPopupMenu.java @@ -120,7 +120,7 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement private boolean lightWeightPopupEnabled; /** SelectionModel that keeps track of menu selection. */ - private SingleSelectionModel selectionModel; + protected SingleSelectionModel selectionModel; /* Popup that is used to display JPopupMenu */ private transient Popup popup; diff --git a/libjava/classpath/javax/swing/JRadioButtonMenuItem.java b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java index 0d7c1d10533..13ef189a514 100644 --- a/libjava/classpath/javax/swing/JRadioButtonMenuItem.java +++ b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java @@ -144,6 +144,7 @@ public class JRadioButtonMenuItem extends JMenuItem implements Accessible super(text, icon); setModel(new JToggleButton.ToggleButtonModel()); model.setSelected(selected); + setFocusable(false); } /** diff --git a/libjava/classpath/javax/swing/JSplitPane.java b/libjava/classpath/javax/swing/JSplitPane.java index 2747686a31a..5b77f5176ae 100644 --- a/libjava/classpath/javax/swing/JSplitPane.java +++ b/libjava/classpath/javax/swing/JSplitPane.java @@ -708,7 +708,8 @@ public class JSplitPane extends JComponent implements Accessible throw new IllegalArgumentException ("proportion has to be between 0 and 1."); - int max = (orientation == HORIZONTAL_SPLIT) ? getWidth() : getHeight(); + int max = ((orientation == HORIZONTAL_SPLIT) ? getWidth() : getHeight()) + - getDividerSize(); setDividerLocation((int) (proportionalLocation * max)); } diff --git a/libjava/classpath/javax/swing/JTabbedPane.java b/libjava/classpath/javax/swing/JTabbedPane.java index ee6af857ee3..5c8d0474852 100644 --- a/libjava/classpath/javax/swing/JTabbedPane.java +++ b/libjava/classpath/javax/swing/JTabbedPane.java @@ -990,6 +990,8 @@ public class JTabbedPane extends JComponent implements Serializable, checkIndex(index, -1, tabs.size()); if (index != getSelectedIndex()) { + // Hiding and showing the involved components + // is done by the JTabbedPane's UI. model.setSelectedIndex(index); } } @@ -1247,7 +1249,32 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void remove(Component component) { - super.remove(component); + // Since components implementing UIResource + // are not added as regular tabs by the add() + // methods we have to take special care when + // removing these object. Especially + // Container.remove(Component) cannot be used + // because it will call JTabbedPane.remove(int) + // later which is overridden and can only + // handle tab components. + // This implementation can even cope with a + // situation that someone called insertTab() + // with a component that implements UIResource. + int index = indexOfComponent(component); + + // If the component is not a tab component + // find out its Container-given index + // and call that class' implementation + // directly. + if (index == -1) + { + Component[] cs = getComponents(); + for (int i = 0; i< cs.length; i++) + if (cs[i] == component) + super.remove(i); + } + else + removeTabAt(index); } /** @@ -1257,7 +1284,6 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void remove(int index) { - super.remove(index); removeTabAt(index); } diff --git a/libjava/classpath/javax/swing/JTable.java b/libjava/classpath/javax/swing/JTable.java index 855530881f5..5c7bff5d019 100644 --- a/libjava/classpath/javax/swing/JTable.java +++ b/libjava/classpath/javax/swing/JTable.java @@ -2922,56 +2922,189 @@ public class JTable { // update the column model from the table model if the structure has // changed and the flag autoCreateColumnsFromModel is set - if ((event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW)) - && autoCreateColumnsFromModel) + if (event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW)) + handleCompleteChange(event); + else if (event.getType() == TableModelEvent.INSERT) + handleInsert(event); + else if (event.getType() == TableModelEvent.DELETE) + handleDelete(event); + else + handleUpdate(event); + } + + /** + * Handles a request for complete relayout. This is the case when + * event.getFirstRow() == TableModelEvent.HEADER_ROW. + * + * @param ev the table model event + */ + private void handleCompleteChange(TableModelEvent ev) + { + clearSelection(); + checkSelection(); + rowHeights = null; + if (getAutoCreateColumnsFromModel()) + createDefaultColumnsFromModel(); + else + resizeAndRepaint(); + } + + /** + * Handles table model insertions. + * + * @param ev the table model event + */ + private void handleInsert(TableModelEvent ev) + { + // Sync selection model with data model. + int first = ev.getFirstRow(); + if (first < 0) + first = 0; + int last = ev.getLastRow(); + if (last < 0) + last = getRowCount() - 1; + selectionModel.insertIndexInterval(first, last - first + 1, true); + checkSelection(); + + // For variable height rows we must update the SizeSequence thing. + if (rowHeights != null) { - rowHeights = null; - if (getAutoCreateColumnsFromModel()) - createDefaultColumnsFromModel(); - resizeAndRepaint(); - return; + rowHeights.insertEntries(first, last - first + 1, rowHeight); + // TODO: We repaint the whole thing when the rows have variable + // heights. We might want to handle this better though. + repaint(); + } + else + { + // Repaint the dirty region and revalidate. + int rowHeight = getRowHeight(); + Rectangle dirty = new Rectangle(0, first * rowHeight, + getColumnModel().getTotalColumnWidth(), + (getRowCount() - first) * rowHeight); + repaint(dirty); + } + revalidate(); + } + + /** + * Handles table model deletions. + * + * @param ev the table model event + */ + private void handleDelete(TableModelEvent ev) + { + // Sync selection model with data model. + int first = ev.getFirstRow(); + if (first < 0) + first = 0; + int last = ev.getLastRow(); + if (last < 0) + last = getRowCount() - 1; + + selectionModel.removeIndexInterval(first, last); + + checkSelection(); + + if (dataModel.getRowCount() == 0) + clearSelection(); + + // For variable height rows we must update the SizeSequence thing. + if (rowHeights != null) + { + rowHeights.removeEntries(first, last - first + 1); + // TODO: We repaint the whole thing when the rows have variable + // heights. We might want to handle this better though. + repaint(); + } + else + { + // Repaint the dirty region and revalidate. + int rowHeight = getRowHeight(); + int oldRowCount = getRowCount() + last - first + 1; + Rectangle dirty = new Rectangle(0, first * rowHeight, + getColumnModel().getTotalColumnWidth(), + (oldRowCount - first) * rowHeight); + repaint(dirty); } + revalidate(); + } - // If the structure changes, we need to revalidate, since that might - // affect the size parameters of the JTable. Otherwise we only need - // to perform a repaint to update the view. - if (event == null || event.getType() == TableModelEvent.INSERT) + /** + * Handles table model updates without structural changes. + * + * @param ev the table model event + */ + private void handleUpdate(TableModelEvent ev) + { + if (rowHeights == null) { - // Sync selection model with data model. - if (event != null) + // Some cells have been changed without changing the structure. + // Figure out the dirty rectangle and repaint. + int firstRow = ev.getFirstRow(); + int lastRow = ev.getLastRow(); + int col = ev.getColumn(); + Rectangle dirty; + if (col == TableModelEvent.ALL_COLUMNS) { - int first = event.getFirstRow(); - if (first < 0) - first = 0; - int last = event.getLastRow(); - if (last < 0) - last = getRowCount() - 1; - selectionModel.insertIndexInterval(first, last - first + 1, true); - if (rowHeights != null) - rowHeights.insertEntries(first, last - first + 1, rowHeight); + // All columns changed. + dirty = new Rectangle(0, firstRow * getRowHeight(), + getColumnModel().getTotalColumnWidth(), 0); } - revalidate(); + else + { + // Only one cell or column of cells changed. + // We need to convert to view column first. + int column = convertColumnIndexToModel(col); + dirty = getCellRect(firstRow, column, false); + } + + // Now adjust the height of the dirty region. + dirty.height = (lastRow + 1) * getRowHeight(); + // .. and repaint. + repaint(dirty); } - if (event == null || event.getType() == TableModelEvent.DELETE) + else { - // Sync selection model with data model. - if (event != null) + // TODO: We repaint the whole thing when the rows have variable + // heights. We might want to handle this better though. + repaint(); + } + } + + /** + * Helper method for adjusting the lead and anchor indices when the + * table structure changed. This sets the lead and anchor to -1 if there's + * no more rows, or set them to 0 when they were at -1 and there are actually + * some rows now. + */ + private void checkSelection() + { + TableModel m = getModel(); + ListSelectionModel sm = selectionModel; + if (m != null) + { + int lead = sm.getLeadSelectionIndex(); + int c = m.getRowCount(); + if (c == 0 && lead != -1) + { + // No rows in the model, reset lead and anchor to -1. + sm.setValueIsAdjusting(true); + sm.setAnchorSelectionIndex(-1); + sm.setLeadSelectionIndex(-1); + sm.setValueIsAdjusting(false); + } + else if (c != 0 && lead == -1) { - int first = event.getFirstRow(); - if (first < 0) - first = 0; - int last = event.getLastRow(); - if (last < 0) - last = getRowCount() - 1; - selectionModel.removeIndexInterval(first, last); - if (rowHeights != null) - rowHeights.removeEntries(first, last - first + 1); + // We have rows, but no lead/anchor. Set them to 0. We + // do a little trick here so that the actual selection is not + // touched. + if (sm.isSelectedIndex(0)) + sm.addSelectionInterval(0, 0); + else + sm.removeSelectionInterval(0, 0); } - if (dataModel.getRowCount() == 0) - clearSelection(); - revalidate(); + // Nothing to do in the other cases. } - repaint(); } /** @@ -3468,6 +3601,8 @@ public class JTable * Get the value of the {@link #rowSelectionAllowed} property. * * @return The current value of the property + * + * @see #setRowSelectionAllowed(boolean) */ public boolean getRowSelectionAllowed() { @@ -3621,6 +3756,8 @@ public class JTable * Get the value of the <code>columnSelectionAllowed</code> property. * * @return The current value of the columnSelectionAllowed property + * + * @see #setColumnSelectionAllowed(boolean) */ public boolean getColumnSelectionAllowed() { @@ -3874,11 +4011,17 @@ public class JTable * Set the value of the {@link #rowSelectionAllowed} property. * * @param r The new value of the rowSelectionAllowed property + * + * @see #getRowSelectionAllowed() */ public void setRowSelectionAllowed(boolean r) { - rowSelectionAllowed = r; - repaint(); + if (rowSelectionAllowed != r) + { + rowSelectionAllowed = r; + firePropertyChange("rowSelectionAllowed", !r, r); + repaint(); + } } /** @@ -3988,11 +4131,17 @@ public class JTable * Set the value of the <code>columnSelectionAllowed</code> property. * * @param c The new value of the property + * + * @see #getColumnSelectionAllowed() */ public void setColumnSelectionAllowed(boolean c) { - getColumnModel().setColumnSelectionAllowed(c); - repaint(); + if (columnModel.getColumnSelectionAllowed() != c) + { + columnModel.setColumnSelectionAllowed(c); + firePropertyChange("columnSelectionAllowed", !c, c); + repaint(); + } } /** @@ -4014,6 +4163,7 @@ public class JTable if (s != null) s.addListSelectionListener(this); selectionModel = s; + checkSelection(); } /** diff --git a/libjava/classpath/javax/swing/JTree.java b/libjava/classpath/javax/swing/JTree.java index f7583ad69e2..fa898c5a940 100644 --- a/libjava/classpath/javax/swing/JTree.java +++ b/libjava/classpath/javax/swing/JTree.java @@ -1392,9 +1392,9 @@ public class JTree extends JComponent implements Scrollable, Accessible public static final String EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths"; - private static final Object EXPANDED = new Object(); + private static final Object EXPANDED = Boolean.TRUE; - private static final Object COLLAPSED = new Object(); + private static final Object COLLAPSED = Boolean.FALSE; private boolean dragEnabled; @@ -1515,6 +1515,9 @@ public class JTree extends JComponent implements Scrollable, Accessible // The root node appears expanded by default. nodeStates = new Hashtable(); + // The cell renderer gets set by the UI. + cellRenderer = null; + // Install the UI before installing the model. This way we avoid double // initialization of lots of UI and model stuff inside the UI and related // classes. The necessary UI updates are performed via property change diff --git a/libjava/classpath/javax/swing/LookAndFeel.java b/libjava/classpath/javax/swing/LookAndFeel.java index 7f59f1b5f93..be543439636 100644 --- a/libjava/classpath/javax/swing/LookAndFeel.java +++ b/libjava/classpath/javax/swing/LookAndFeel.java @@ -242,15 +242,15 @@ public abstract class LookAndFeel { if (keys == null) return; - for (int i = 0; i < keys.length - 1; i+= 2) + for (int i = 0; i < keys.length - 1; i += 2) { Object key = keys[i]; KeyStroke keyStroke; if (key instanceof KeyStroke) - keyStroke = (KeyStroke)key; + keyStroke = (KeyStroke) key; else - keyStroke = KeyStroke.getKeyStroke((String)key); - retMap.put(keyStroke, keys[i+1]); + keyStroke = KeyStroke.getKeyStroke((String) key); + retMap.put(keyStroke, keys[i + 1]); } } @@ -328,15 +328,15 @@ public abstract class LookAndFeel { JTextComponent.KeyBinding[] retBindings = new JTextComponent.KeyBinding[keyBindingList.length / 2]; - for (int i = 0; i < keyBindingList.length - 1; i+= 2) + for (int i = 0; i < keyBindingList.length - 1; i += 2) { KeyStroke stroke; if (keyBindingList[i] instanceof KeyStroke) - stroke = (KeyStroke)keyBindingList[i]; + stroke = (KeyStroke) keyBindingList[i]; else - stroke = KeyStroke.getKeyStroke((String)keyBindingList[i]); - retBindings[i/2] = new JTextComponent.KeyBinding(stroke, - (String) keyBindingList[i+1]); + stroke = KeyStroke.getKeyStroke((String) keyBindingList[i]); + retBindings[i / 2] = new JTextComponent.KeyBinding(stroke, + (String) keyBindingList[i + 1]); } return retBindings; } diff --git a/libjava/classpath/javax/swing/ProgressMonitor.java b/libjava/classpath/javax/swing/ProgressMonitor.java index 28d22e8a63e..b4c3d222349 100644 --- a/libjava/classpath/javax/swing/ProgressMonitor.java +++ b/libjava/classpath/javax/swing/ProgressMonitor.java @@ -142,12 +142,12 @@ public class ProgressMonitor */ public void close() { - if ( progressDialog != null ) + if (progressDialog != null) { progressDialog.setVisible(false); } - if ( timer != null ) + if (timer != null) { timer.stop(); timer = null; @@ -173,7 +173,7 @@ public class ProgressMonitor // Initializes and starts a timer with a task // which measures the duration and displays // a progress dialog if neccessary. - if ( timer == null && progressDialog == null ) + if (timer == null && progressDialog == null) { timer = new Timer(25, null); timer.addActionListener(new TimerListener()); @@ -182,7 +182,7 @@ public class ProgressMonitor // Cancels timer and hides progress dialog if the // maximum value is reached. - if ( progressBar != null && this.progress >= progressBar.getMaximum() ) + if (progressBar != null && this.progress >= progressBar.getMaximum()) { // The reason for using progressBar.getMaximum() instead of max is that // we want to prevent that changes to the value have any effect after the @@ -326,7 +326,7 @@ public class ProgressMonitor */ public void setNote(String note) { - if ( noteLabel != null ) + if (noteLabel != null) { noteLabel.setText(note); } @@ -401,18 +401,18 @@ public class ProgressMonitor { long now = System.currentTimeMillis(); - if ( first ) + if (first) { - if (( now - timestamp ) > millisToDecideToPopup ) + if ((now - timestamp) > millisToDecideToPopup) { first = false; - long expected = ( progress - min == 0 ) ? - ( now - timestamp ) * ( max - min ) : - ( now - timestamp ) * ( max - min ) / ( progress - min ); + long expected = (progress - min == 0) ? + (now - timestamp) * (max - min) : + (now - timestamp) * (max - min) / (progress - min); - if ( expected > millisToPopup ) + if (expected > millisToPopup) { createDialog(); } @@ -424,14 +424,14 @@ public class ProgressMonitor return; } } - else if ( progressDialog != null ) + else if (progressDialog != null) { // The progress dialog is being displayed. We now calculate // whether setting the progress bar to the current progress // value would result in a visual difference. int delta = progress - progressBar.getValue(); - if ( ( delta * progressBar.getWidth() / (max - min) ) > 0 ) + if ((delta * progressBar.getWidth() / (max - min)) > 0) { // At least one pixel would change. progressBar.setValue(progress); diff --git a/libjava/classpath/javax/swing/ProgressMonitorInputStream.java b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java index fec5c2ee03b..90e721247b1 100644 --- a/libjava/classpath/javax/swing/ProgressMonitorInputStream.java +++ b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java @@ -240,7 +240,7 @@ public class ProgressMonitorInputStream extends FilterInputStream private void checkMonitorCanceled() throws InterruptedIOException { - if ( monitor.isCanceled() ) + if (monitor.isCanceled()) { throw new InterruptedIOException("ProgressMonitor was canceled"); } diff --git a/libjava/classpath/javax/swing/RepaintManager.java b/libjava/classpath/javax/swing/RepaintManager.java index f95934123ea..80f0a3481cd 100644 --- a/libjava/classpath/javax/swing/RepaintManager.java +++ b/libjava/classpath/javax/swing/RepaintManager.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing; -import java.applet.Applet; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; @@ -314,26 +313,45 @@ public class RepaintManager */ public void addInvalidComponent(JComponent component) { - Component ancestor = component; - - while (ancestor != null - && (! (ancestor instanceof JComponent) - || ! ((JComponent) ancestor).isValidateRoot() )) - ancestor = ancestor.getParent(); + Component validateRoot = null; + Component c = component; + while (c != null) + { + // Special cases we don't bother validating are when the invalidated + // component (or any of it's ancestors) is inside a CellRendererPane + // or if it doesn't have a peer yet (== not displayable). + if (c instanceof CellRendererPane || ! c.isDisplayable()) + return; + if (c instanceof JComponent && ((JComponent) c).isValidateRoot()) + { + validateRoot = c; + break; + } - if (ancestor != null - && ancestor instanceof JComponent - && ((JComponent) ancestor).isValidateRoot()) - component = (JComponent) ancestor; + c = c.getParent(); + } - if (invalidComponents.contains(component)) + // If we didn't find a validate root, then we don't validate. + if (validateRoot == null) return; - synchronized (invalidComponents) + // Make sure the validate root and all of it's ancestors are visible. + c = validateRoot; + while (c != null) { - invalidComponents.add(component); + if (! c.isVisible() || ! c.isDisplayable()) + return; + c = c.getParent(); } + if (invalidComponents.contains(validateRoot)) + return; + + //synchronized (invalidComponents) + // { + invalidComponents.add(validateRoot); + // } + if (! repaintWorker.isLive()) { repaintWorker.setLive(true); @@ -379,7 +397,7 @@ public class RepaintManager { if (w <= 0 || h <= 0 || !component.isShowing()) return; - + Component parent = component.getParent(); component.computeVisibleRect(rectCache); @@ -444,8 +462,7 @@ public class RepaintManager */ public void markCompletelyDirty(JComponent component) { - Rectangle r = component.getBounds(); - addDirtyRegion(component, 0, 0, r.width, r.height); + addDirtyRegion(component, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE); } /** @@ -483,13 +500,11 @@ public class RepaintManager */ public boolean isCompletelyDirty(JComponent component) { - boolean retVal = false; - if (dirtyComponents.containsKey(component)) - { - Rectangle dirtyRegion = (Rectangle) dirtyComponents.get(component); - retVal = dirtyRegion.equals(SwingUtilities.getLocalBounds(component)); - } - return retVal; + boolean dirty = false; + Rectangle r = getDirtyRegion(component); + if(r.width == Integer.MAX_VALUE && r.height == Integer.MAX_VALUE) + dirty = true; + return dirty; } /** @@ -618,7 +633,7 @@ public class RepaintManager public Image getOffscreenBuffer(Component component, int proposedWidth, int proposedHeight) { - Component root = getRoot(component); + Component root = SwingUtilities.getWindowAncestor(component); Image buffer = (Image) offscreenBuffers.get(root); if (buffer == null || buffer.getWidth(null) < proposedWidth @@ -628,38 +643,12 @@ public class RepaintManager width = Math.min(doubleBufferMaximumSize.width, width); int height = Math.max(proposedHeight, root.getHeight()); height = Math.min(doubleBufferMaximumSize.height, height); - buffer = component.createImage(width, height); + buffer = root.createImage(width, height); offscreenBuffers.put(root, buffer); } return buffer; } - - /** - * Gets the root of the component given. If a parent of the - * component is an instance of Applet, then the applet is - * returned. The applet is considered the root for painting. - * Otherwise, the root Window is returned if it exists. - * - * @param comp - The component to get the root for. - * @return the parent root. An applet if it is a parent, - * or the root window. If neither exist, null is returned. - */ - private Component getRoot(Component comp) - { - Applet app = null; - - while (comp != null) - { - if (app == null && comp instanceof Window) - return comp; - else if (comp instanceof Applet) - app = (Applet) comp; - comp = comp.getParent(); - } - - return app; - } - + /** * Blits the back buffer of the specified root component to the screen. If * the RepaintManager is currently working on a paint request, the commit @@ -667,67 +656,98 @@ public class RepaintManager * done (by {@link #commitRemainingBuffers}). This is package private because * it must get called by JComponent. * - * @param root the component, either a Window or an Applet instance - * @param area the area to paint on screen + * @param comp the component to be painted + * @param area the area to paint on screen, in comp coordinates */ - void commitBuffer(Component root, Rectangle area) + void commitBuffer(Component comp, Rectangle area) { + // Determine the component that we finally paint the buffer upon. + // We need to paint on the nearest heavyweight component, so that Swing + // hierarchies inside (non-window) heavyweights get painted correctly. + // Otherwise we would end up blitting the backbuffer behind the heavyweight + // which is wrong. + Component root = getHeavyweightParent(comp); + // FIXME: Optimize this. + Rectangle rootRect = SwingUtilities.convertRectangle(comp, area, root); + // We synchronize on dirtyComponents here because that is what // paintDirtyRegions also synchronizes on while painting. synchronized (dirtyComponents) { // If the RepaintManager is not currently painting, then directly // blit the requested buffer on the screen. - if (! repaintUnderway) + if (true || ! repaintUnderway) { - Graphics g = root.getGraphics(); - Image buffer = (Image) offscreenBuffers.get(root); - Rectangle clip = g.getClipBounds(); - if (clip != null) - area = SwingUtilities.computeIntersection(clip.x, clip.y, - clip.width, clip.height, - area); - int dx1 = area.x; - int dy1 = area.y; - int dx2 = area.x + area.width; - int dy2 = area.y + area.height; - // Make sure we have a sane clip at this point. - g.clipRect(area.x, area.y, area.width, area.height); - - // Make sure the coordinates are inside the buffer, everything else - // might lead to problems. - // TODO: This code should not really be necessary, however, in fact - // we have two issues here: - // 1. We shouldn't get repaint requests in areas outside the buffer - // region in the first place. This still happens for example - // when a component is inside a JViewport, and the component has - // a size that would reach beyond the window size. - // 2. Graphics.drawImage() should not behave strange when trying - // to draw regions outside the image. - int bufferWidth = buffer.getWidth(root); - int bufferHeight = buffer.getHeight(root); - dx1 = Math.min(bufferWidth, dx1); - dy1 = Math.min(bufferHeight, dy1); - dx2 = Math.min(bufferWidth, dx2); - dy2 = Math.min(bufferHeight, dy2); - g.drawImage(buffer, 0, 0, root); - g.dispose(); + blitBuffer(root, rootRect); } + // Otherwise queue this request up, until all the RepaintManager work // is done. else { if (commitRequests.containsKey(root)) - SwingUtilities.computeUnion(area.x, area.y, area.width, - area.height, + SwingUtilities.computeUnion(rootRect.x, rootRect.y, + rootRect.width, rootRect.height, (Rectangle) commitRequests.get(root)); else - commitRequests.put(root, area); + commitRequests.put(root, rootRect); } } } /** + * Copies the buffer to the screen. Note that the root component here is + * not necessarily the component with which the offscreen buffer is + * associated. The offscreen buffers are always allocated for the toplevel + * windows. However, painted is performed on lower-level heavyweight + * components too, if they contain Swing components. + * + * @param root the heavyweight component to blit upon + * @param rootRect the rectangle in the root component's coordinate space + */ + private void blitBuffer(Component root, Rectangle rootRect) + { + if (! root.isShowing()) + return; + + // Find the Window from which we use the backbuffer. + Component bufferRoot = root; + Rectangle bufferRect = rootRect.getBounds(); + if (!(bufferRoot instanceof Window)) + { + bufferRoot = SwingUtilities.getWindowAncestor(bufferRoot); + SwingUtilities.convertRectangleToAncestor(root, rootRect, bufferRoot); + } + + Graphics g = root.getGraphics(); + Image buffer = (Image) offscreenBuffers.get(bufferRoot); + + // Make sure we have a sane clip at this point. + g.clipRect(rootRect.x, rootRect.y, rootRect.width, rootRect.height); + g.drawImage(buffer, rootRect.x - bufferRect.x, rootRect.y - bufferRect.y, + root); + g.dispose(); + + } + + /** + * Finds and returns the nearest heavyweight parent for the specified + * component. If the component isn't contained inside a heavyweight parent, + * this returns null. + * + * @param comp the component + * + * @return the nearest heavyweight parent for the specified component or + * null if the component has no heavyweight ancestor + */ + private Component getHeavyweightParent(Component comp) + { + while (comp != null && comp.isLightweight()) + comp = comp.getParent(); + return comp; + } + + /** * Commits the queued up back buffers to screen all at once. */ private void commitRemainingBuffers() @@ -743,7 +763,7 @@ public class RepaintManager Map.Entry entry = (Map.Entry) i.next(); Component root = (Component) entry.getKey(); Rectangle area = (Rectangle) entry.getValue(); - commitBuffer(root, area); + blitBuffer(root, area); i.remove(); } } @@ -767,7 +787,7 @@ public class RepaintManager public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth, int proposedHeight) { - Component root = getRoot(comp); + Component root = SwingUtilities.getWindowAncestor(comp); Image buffer = (Image) offscreenBuffers.get(root); if (buffer == null || buffer.getWidth(null) < proposedWidth diff --git a/libjava/classpath/javax/swing/ScrollPaneLayout.java b/libjava/classpath/javax/swing/ScrollPaneLayout.java index 31846fa557d..8ce8fd86f7a 100644 --- a/libjava/classpath/javax/swing/ScrollPaneLayout.java +++ b/libjava/classpath/javax/swing/ScrollPaneLayout.java @@ -82,7 +82,8 @@ public class ScrollPaneLayout // Nothing to do here. } - public void syncWithScrollPane(JScrollPane scrollPane) { + public void syncWithScrollPane(JScrollPane scrollPane) + { viewport = scrollPane.getViewport(); rowHead = scrollPane.getRowHeader(); colHead = scrollPane.getColumnHeader(); @@ -145,7 +146,8 @@ public class ScrollPaneLayout throw new IllegalArgumentException(); } - public void removeLayoutComponent(Component component) { + public void removeLayoutComponent(Component component) + { if (component == viewport) viewport = null; else if (component == vsb) @@ -448,7 +450,8 @@ public class ScrollPaneLayout * @deprecated As of Swing 1.1 replaced by * {@link javax.swing.JScrollPane#getViewportBorderBounds}. */ - public Rectangle getViewportBorderBounds(JScrollPane scrollPane) { + public Rectangle getViewportBorderBounds(JScrollPane scrollPane) + { return null; } diff --git a/libjava/classpath/javax/swing/SizeSequence.java b/libjava/classpath/javax/swing/SizeSequence.java index 26099a15461..a5f34710c76 100644 --- a/libjava/classpath/javax/swing/SizeSequence.java +++ b/libjava/classpath/javax/swing/SizeSequence.java @@ -37,6 +37,8 @@ exception statement from your version. */ package javax.swing; +import java.util.Arrays; + /** * A sequence of values that represent the dimensions (widths or heights) of * some collection of items (for example, the widths of the columns in a table). @@ -80,8 +82,8 @@ public class SizeSequence */ public SizeSequence(int numEntries, int value) { - sizes = new int[0]; - insertEntries(0, numEntries, value); + sizes = new int[numEntries]; + Arrays.fill(sizes, value); } /** diff --git a/libjava/classpath/javax/swing/SpringLayout.java b/libjava/classpath/javax/swing/SpringLayout.java index d87050639fe..2595b196703 100644 --- a/libjava/classpath/javax/swing/SpringLayout.java +++ b/libjava/classpath/javax/swing/SpringLayout.java @@ -505,7 +505,7 @@ public class SpringLayout implements LayoutManager2 } } - private static abstract class DeferredDimension extends Spring + private abstract static class DeferredDimension extends Spring { private int value; diff --git a/libjava/classpath/javax/swing/SwingUtilities.java b/libjava/classpath/javax/swing/SwingUtilities.java index 5d02d9bb396..ccd37d03a55 100644 --- a/libjava/classpath/javax/swing/SwingUtilities.java +++ b/libjava/classpath/javax/swing/SwingUtilities.java @@ -1045,8 +1045,7 @@ public class SwingUtilities */ public static boolean isLeftMouseButton(MouseEvent event) { - return ((event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) - == InputEvent.BUTTON1_DOWN_MASK); + return ((event.getModifiers() & InputEvent.BUTTON1_MASK) != 0); } /** @@ -1599,4 +1598,27 @@ public class SwingUtilities throw new IllegalArgumentException("Unrecognised code: " + code); } } + + /** + * Converts a rectangle in the coordinate system of a child component into + * a rectangle of one of it's Ancestors. The result is stored in the input + * rectangle. + * + * @param comp the child component + * @param r the rectangle to convert + * @param ancestor the ancestor component + */ + static void convertRectangleToAncestor(Component comp, Rectangle r, + Component ancestor) + { + if (comp == ancestor) + return; + + r.x += comp.getX(); + r.y += comp.getY(); + + Component parent = comp.getParent(); + if (parent != null && parent != ancestor) + convertRectangleToAncestor(parent, r, ancestor); + } } diff --git a/libjava/classpath/javax/swing/Timer.java b/libjava/classpath/javax/swing/Timer.java index 231b71d73bb..acd22624947 100644 --- a/libjava/classpath/javax/swing/Timer.java +++ b/libjava/classpath/javax/swing/Timer.java @@ -1,5 +1,5 @@ /* Timer.java -- - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -264,9 +264,13 @@ public class Timer * firing the first event. * * @param d The time gap between the subsequent events, in milliseconds + * + * @throws IllegalArgumentException if <code>d</code> is less than zero. */ public void setDelay(int d) { + if (d < 0) + throw new IllegalArgumentException("Invalid delay: " + d); delay = d; } @@ -287,9 +291,13 @@ public class Timer * subsequent events. * * @param i the initial delay, in milliseconds + * + * @throws IllegalArgumentException if <code>i</code> is less than zero. */ public void setInitialDelay(int i) { + if (i < 0) + throw new IllegalArgumentException("Invalid initial delay: " + i); initialDelay = i; } diff --git a/libjava/classpath/javax/swing/ToolTipManager.java b/libjava/classpath/javax/swing/ToolTipManager.java index c7de4db8330..963ccf88117 100644 --- a/libjava/classpath/javax/swing/ToolTipManager.java +++ b/libjava/classpath/javax/swing/ToolTipManager.java @@ -1,5 +1,5 @@ /* ToolTipManager.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -267,10 +267,12 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener } /** - * This method sets the initial delay before the ToolTip is shown when the + * Sets the initial delay before the ToolTip is shown when the * mouse enters a Component. * * @param delay The initial delay before the ToolTip is shown. + * + * @throws IllegalArgumentException if <code>delay</code> is less than zero. */ public void setInitialDelay(int delay) { @@ -289,9 +291,11 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener } /** - * This method sets the time the ToolTip will be shown before being hidden. + * Sets the time the ToolTip will be shown before being hidden. * - * @param delay The time the ToolTip will be shown before being hidden. + * @param delay the delay (in milliseconds) before tool tips are hidden. + * + * @throws IllegalArgumentException if <code>delay</code> is less than zero. */ public void setDismissDelay(int delay) { @@ -310,10 +314,12 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener } /** - * This method sets the amount of delay where if the mouse re-enters a + * Sets the amount of delay where if the mouse re-enters a * Component, the tooltip will be shown immediately. * - * @param delay The reshow delay. + * @param delay The reshow delay (in milliseconds). + * + * @throws IllegalArgumentException if <code>delay</code> is less than zero. */ public void setReshowDelay(int delay) { diff --git a/libjava/classpath/javax/swing/UIManager.java b/libjava/classpath/javax/swing/UIManager.java index e6f80116321..77be44afcbb 100644 --- a/libjava/classpath/javax/swing/UIManager.java +++ b/libjava/classpath/javax/swing/UIManager.java @@ -45,6 +45,7 @@ import java.awt.Insets; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.Serializable; +import java.util.Enumeration; import java.util.Locale; import javax.swing.border.Border; @@ -117,6 +118,87 @@ public class UIManager implements Serializable } } + /** + * A UIDefaults subclass that multiplexes between itself and a 'fallback' + * UIDefaults instance. This is used to protect the L&F UIDefaults from beeing + * overwritten by applications. + */ + private static class MultiplexUIDefaults + extends UIDefaults + { + private class MultiplexEnumeration + implements Enumeration + { + Enumeration[] enums; + int i; + MultiplexEnumeration(Enumeration e1, Enumeration e2) + { + enums = new Enumeration[]{ e1, e2 }; + i = 0; + } + + public boolean hasMoreElements() + { + return enums[i].hasMoreElements() || i < enums.length - 1; + } + + public Object nextElement() + { + Object val = enums[i].nextElement(); + if (! enums[i].hasMoreElements() && i < enums.length - 1) + i++; + return val; + } + + } + + UIDefaults fallback; + + MultiplexUIDefaults(UIDefaults d) + { + fallback = d; + } + + public Object get(Object key) + { + Object val = super.get(key); + if (val == null) + val = fallback.get(key); + return val; + } + + public Object get(Object key, Locale l) + { + Object val = super.get(key, l); + if (val == null) + val = fallback.get(key, l); + return val; + } + + public Object remove(Object key) + { + Object val = super.remove(key); + if (val == null) + val = fallback.remove(key); + return val; + } + + public int size() + { + return super.size() + fallback.size(); + } + + public Enumeration keys() + { + return new MultiplexEnumeration(super.keys(), fallback.keys()); + } + + public Enumeration elements() + { + return new MultiplexEnumeration(super.elements(), fallback.elements()); + } + } + private static final long serialVersionUID = -5547433830339189365L; /** The installed look and feel(s). */ @@ -131,12 +213,9 @@ public class UIManager implements Serializable /** The current look and feel. */ static LookAndFeel currentLookAndFeel; - static UIDefaults currentUIDefaults; + static MultiplexUIDefaults currentUIDefaults; - /** - * UIDefaults set by the user. - */ - static UIDefaults userUIDefaults; + static UIDefaults lookAndFeelDefaults; /** Property change listener mechanism. */ static PropertyChangeSupport listeners @@ -149,9 +228,7 @@ public class UIManager implements Serializable { if (defaultlaf != null) { - Class lafClass = Class.forName(defaultlaf); - LookAndFeel laf = (LookAndFeel) lafClass.newInstance(); - setLookAndFeel(laf); + setLookAndFeel(defaultlaf); } else { @@ -162,6 +239,7 @@ public class UIManager implements Serializable { System.err.println("cannot initialize Look and Feel: " + defaultlaf); System.err.println("error: " + ex.toString()); + ex.printStackTrace(); System.err.println("falling back to Metal Look and Feel"); try { @@ -312,12 +390,7 @@ public class UIManager implements Serializable */ public static Object get(Object key) { - Object val = null; - if (userUIDefaults != null) - val = userUIDefaults.get(key); - if (val == null) - val = getLookAndFeelDefaults().get(key); - return val; + return getDefaults().get(key); } /** @@ -330,12 +403,7 @@ public class UIManager implements Serializable */ public static Object get(Object key, Locale locale) { - Object val = null; - if (userUIDefaults != null) - val = userUIDefaults.get(key, locale); - if (val == null) - val = getLookAndFeelDefaults().get(key, locale); - return val; + return getDefaults().get(key, locale); } /** @@ -414,6 +482,8 @@ public class UIManager implements Serializable */ public static UIDefaults getDefaults() { + if (currentUIDefaults == null) + currentUIDefaults = new MultiplexUIDefaults(null); return currentUIDefaults; } @@ -546,7 +616,7 @@ public class UIManager implements Serializable */ public static UIDefaults getLookAndFeelDefaults() { - return currentUIDefaults; + return lookAndFeelDefaults; } /** @@ -587,13 +657,7 @@ public class UIManager implements Serializable */ public static ComponentUI getUI(JComponent target) { - ComponentUI ui = null; - if (userUIDefaults != null - && userUIDefaults.get(target.getUIClassID()) != null) - ui = userUIDefaults.getUI(target); - if (ui == null) - ui = currentUIDefaults.getUI(target); - return ui; + return getDefaults().getUI(target); } /** @@ -625,11 +689,7 @@ public class UIManager implements Serializable */ public static Object put(Object key, Object value) { - Object old = get(key); - if (userUIDefaults == null) - userUIDefaults = new UIDefaults(); - userUIDefaults.put(key, value); - return old; + return getDefaults().put(key, value); } /** @@ -654,7 +714,8 @@ public class UIManager implements Serializable throws UnsupportedLookAndFeelException { if (newLookAndFeel != null && ! newLookAndFeel.isSupportedLookAndFeel()) - throw new UnsupportedLookAndFeelException(newLookAndFeel.getName()); + throw new UnsupportedLookAndFeelException(newLookAndFeel.getName() + + " not supported on this platform"); LookAndFeel oldLookAndFeel = currentLookAndFeel; if (oldLookAndFeel != null) oldLookAndFeel.uninitialize(); @@ -664,7 +725,12 @@ public class UIManager implements Serializable if (newLookAndFeel != null) { newLookAndFeel.initialize(); - currentUIDefaults = newLookAndFeel.getDefaults(); + lookAndFeelDefaults = newLookAndFeel.getDefaults(); + if (currentUIDefaults == null) + currentUIDefaults = + new MultiplexUIDefaults(lookAndFeelDefaults); + else + currentUIDefaults.fallback = lookAndFeelDefaults; } else { @@ -689,7 +755,8 @@ public class UIManager implements Serializable throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException { - Class c = Class.forName(className); + Class c = Class.forName(className, true, + Thread.currentThread().getContextClassLoader()); LookAndFeel a = (LookAndFeel) c.newInstance(); // throws class-cast-exception setLookAndFeel(a); } diff --git a/libjava/classpath/javax/swing/border/TitledBorder.java b/libjava/classpath/javax/swing/border/TitledBorder.java index 56146e01d1b..38ccd720f16 100644 --- a/libjava/classpath/javax/swing/border/TitledBorder.java +++ b/libjava/classpath/javax/swing/border/TitledBorder.java @@ -1,5 +1,5 @@ /* TitledBorder.java -- - Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -961,7 +961,8 @@ public class TitledBorder extends AbstractBorder public void setTitlePosition(int titlePosition) { if ((titlePosition < DEFAULT_POSITION) || (titlePosition > BELOW_BOTTOM)) - throw new IllegalArgumentException(); + throw new IllegalArgumentException(titlePosition + + " is not a valid title position."); // Swing borders are not JavaBeans, thus no need to fire an event. this.titlePosition = titlePosition; @@ -982,7 +983,8 @@ public class TitledBorder extends AbstractBorder { if ((titleJustification < DEFAULT_JUSTIFICATION) || (titleJustification > TRAILING)) - throw new IllegalArgumentException(); + throw new IllegalArgumentException(titleJustification + + " is not a valid title justification."); // Swing borders are not JavaBeans, thus no need to fire an event. this.titleJustification = titleJustification; diff --git a/libjava/classpath/javax/swing/event/EventListenerList.java b/libjava/classpath/javax/swing/event/EventListenerList.java index 6a2f34ebb3d..bde8b3c7e4f 100644 --- a/libjava/classpath/javax/swing/event/EventListenerList.java +++ b/libjava/classpath/javax/swing/event/EventListenerList.java @@ -108,7 +108,7 @@ public class EventListenerList * An array with all currently registered listeners. The array has * twice as many elements as there are listeners. For an even * integer <code>i</code>, <code>listenerList[i]</code> indicates - * the registered class, and <code>listenerList[i+1]</code> is the + * the registered class, and <code>listenerList[i + 1]</code> is the * listener. */ protected transient Object[] listenerList = NO_LISTENERS; diff --git a/libjava/classpath/javax/swing/event/ListDataEvent.java b/libjava/classpath/javax/swing/event/ListDataEvent.java index 2a6e6dbe9f0..897fc128f20 100644 --- a/libjava/classpath/javax/swing/event/ListDataEvent.java +++ b/libjava/classpath/javax/swing/event/ListDataEvent.java @@ -1,5 +1,5 @@ /* ListDataEvent.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,6 +41,9 @@ package javax.swing.event; import java.util.EventObject; /** + * An event that contains information about a modification to the content of + * a list. + * * @author Andrew Selkirk * @author Ronald Veldema */ @@ -48,32 +51,46 @@ public class ListDataEvent extends EventObject { private static final long serialVersionUID = 2510353260071004774L; + /** An event type indicating that the list content has been modified. */ public static final int CONTENTS_CHANGED = 0; + + /** An event type indicating that an interval has been added to the list. */ public static final int INTERVAL_ADDED = 1; + + /** + * An event type indicating that an interval has been removed from the + * list. + */ public static final int INTERVAL_REMOVED = 2; - private int type = 0; - private int index0 = 0; - private int index1 = 0; + private int type; + private int index0; + private int index1; /** * Creates a <code>ListDataEvent</code> object. * - * @param source The source of the event. - * @param type The type of the event - * @param index0 Bottom of range - * @param index1 Top of range + * @param source the source of the event (<code>null</code> not permitted). + * @param type the type of the event (should be one of + * {@link #CONTENTS_CHANGED}, {@link #INTERVAL_ADDED} or + * {@link #INTERVAL_REMOVED}, although this is not enforced). + * @param index0 the index for one end of the modified range of list + * elements. + * @param index1 the index for the other end of the modified range of list + * elements. */ public ListDataEvent(Object source, int type, int index0, int index1) { super(source); this.type = type; - this.index0 = index0; - this.index1 = index1; + this.index0 = Math.min(index0, index1); + this.index1 = Math.max(index0, index1); } /** - * Returns the bottom index. + * Returns the index of the first item in the range of modified list items. + * + * @return The index of the first item in the range of modified list items. */ public int getIndex0() { @@ -81,7 +98,9 @@ public class ListDataEvent extends EventObject } /** - * Returns the top index. + * Returns the index of the last item in the range of modified list items. + * + * @return The index of the last item in the range of modified list items. */ public int getIndex1() { @@ -89,10 +108,25 @@ public class ListDataEvent extends EventObject } /** - * Returns the type of this event. + * Returns a code representing the type of this event, which is usually one + * of {@link #CONTENTS_CHANGED}, {@link #INTERVAL_ADDED} or + * {@link #INTERVAL_REMOVED}. + * + * @return The event type. */ public int getType() { return type; } + + /** + * Returns a string representing the state of this event. + * + * @return A string. + */ + public String toString() + { + return getClass().getName() + "[type=" + type + ",index0=" + index0 + + ",index1=" + index1 + "]"; + } } diff --git a/libjava/classpath/javax/swing/event/MenuEvent.java b/libjava/classpath/javax/swing/event/MenuEvent.java index 35bb5b97b43..8ba32929e2d 100644 --- a/libjava/classpath/javax/swing/event/MenuEvent.java +++ b/libjava/classpath/javax/swing/event/MenuEvent.java @@ -1,5 +1,5 @@ /* MenuEvent.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,7 +37,6 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.util.EventObject; /** @@ -45,15 +44,16 @@ import java.util.EventObject; * @author Andrew Selkirk * @author Ronald Veldema */ -public class MenuEvent extends EventObject { - - /** - * Constructor MenuEvent - * @param source Source object - */ - public MenuEvent(Object source) { - super(source); - } // MenuEvent() - - -} // MenuEvent +public class MenuEvent extends EventObject +{ + + /** + * Constructor MenuEvent + * @param source Source object + */ + public MenuEvent(Object source) + { + super(source); + } + +} diff --git a/libjava/classpath/javax/swing/event/TreeExpansionListener.java b/libjava/classpath/javax/swing/event/TreeExpansionListener.java index 08507a0ffe9..45a5ef93c6d 100644 --- a/libjava/classpath/javax/swing/event/TreeExpansionListener.java +++ b/libjava/classpath/javax/swing/event/TreeExpansionListener.java @@ -37,26 +37,26 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.util.EventListener; /** * TreeExpansionListener public interface * @author Andrew Selkirk */ -public interface TreeExpansionListener extends EventListener { +public interface TreeExpansionListener extends EventListener +{ - /** - * Tree collapsed - * @param event Tree Expansion Event - */ - void treeCollapsed(TreeExpansionEvent event); + /** + * Tree collapsed + * @param event Tree Expansion Event + */ + void treeCollapsed(TreeExpansionEvent event); - /** - * Tree expanded - * @param event Tree Expansion Event - */ - void treeExpanded(TreeExpansionEvent event); + /** + * Tree expanded + * @param event Tree Expansion Event + */ + void treeExpanded(TreeExpansionEvent event); -} // TreeExpansionListener +} diff --git a/libjava/classpath/javax/swing/filechooser/FileSystemView.java b/libjava/classpath/javax/swing/filechooser/FileSystemView.java index f51b745c892..84b80dd402c 100644 --- a/libjava/classpath/javax/swing/filechooser/FileSystemView.java +++ b/libjava/classpath/javax/swing/filechooser/FileSystemView.java @@ -76,7 +76,10 @@ public abstract class FileSystemView */ public File createFileObject(String path) { - return new File(path); + File f = new File(path); + if (isFileSystemRoot(f)) + f = this.createFileSystemRoot(f); + return f; } /** @@ -223,16 +226,24 @@ public abstract class FileSystemView /** * Returns the name of a file as it would be displayed by the underlying - * system. This implementation returns <code>null</code>, subclasses must - * override. + * system. * * @param f the file. * - * @return <code>null</code>. + * @return the name of a file as it would be displayed by the underlying + * system + * + * @specnote The specification suggests that the information here is + * fetched from a ShellFolder class. This seems to be a non public + * private file handling class. We simply return File.getName() + * here and leave special handling to subclasses. */ public String getSystemDisplayName(File f) { - return null; + String name = null; + if (f != null) + name = f.getName(); + return name; } /** diff --git a/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java b/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java index 96dfd2e1b1a..f8d71e1df33 100644 --- a/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java +++ b/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java @@ -106,17 +106,34 @@ class UnixFileSystemView extends FileSystemView /** * Returns the name of a file as it would be displayed by the underlying - * system. This method is NOT YET IMPLEMENTED. + * system. * * @param f the file. * - * @return <code>null</code>. + * @return the name of a file as it would be displayed by the underlying + * system */ public String getSystemDisplayName(File f) - throws NotImplementedException { - // FIXME: Implement; - return null; + String name = null; + if (f != null) + { + if (isRoot(f)) + name = f.getAbsolutePath(); + else + { + try + { + String path = f.getCanonicalPath(); + name = path.substring(path.lastIndexOf(File.separator) + 1); + } + catch (IOException e) + { + name = f.getName(); + } + } + } + return name; } /** diff --git a/libjava/classpath/javax/swing/plaf/IconUIResource.java b/libjava/classpath/javax/swing/plaf/IconUIResource.java index 659c8e7bab7..8ac9e0875ae 100644 --- a/libjava/classpath/javax/swing/plaf/IconUIResource.java +++ b/libjava/classpath/javax/swing/plaf/IconUIResource.java @@ -1,5 +1,5 @@ /* IconUIResource.java -- - Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -63,7 +63,8 @@ public class IconUIResource implements Icon, UIResource, Serializable /** - * The icon that is wrapped by this <code>IconUIResource</code>. + * The icon that is wrapped by this <code>IconUIResource</code> (never + * <code>null</code>). */ private Icon delegate; @@ -73,10 +74,12 @@ public class IconUIResource implements Icon, UIResource, Serializable * icon. All messages are forwarded to the delegate icon. * * @param delegate the icon that is wrapped by this - * <code>IconUIResource</code>. + * <code>IconUIResource</code> (<code>null</code> not permitted). */ public IconUIResource(Icon delegate) { + if (delegate == null) + throw new IllegalArgumentException("Null 'delegate' argument."); this.delegate = delegate; } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java index f796d9a730a..781269b2adf 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java @@ -1,5 +1,5 @@ /* BasicArrowButton.java -- - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -42,7 +42,6 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Polygon; -import java.awt.Rectangle; import javax.swing.ButtonModel; import javax.swing.JButton; @@ -86,7 +85,9 @@ public class BasicArrowButton extends JButton implements SwingConstants transient Color highlight = Color.WHITE; /** - * Creates a new <code>BasicArrowButton</code> object. + * Creates a new <code>BasicArrowButton</code> object with an arrow pointing + * in the specified direction. If the <code>direction</code> is not one of + * the specified constants, no arrow is drawn. * * @param direction The direction the arrow points in (one of: * {@link #NORTH}, {@link #SOUTH}, {@link #EAST} and {@link #WEST}). @@ -95,6 +96,7 @@ public class BasicArrowButton extends JButton implements SwingConstants { super(); setDirection(direction); + setFocusable(false); } /** @@ -116,8 +118,7 @@ public class BasicArrowButton extends JButton implements SwingConstants this.shadow = shadow; this.darkShadow = darkShadow; this.highlight = highlight; - // Mark the button as not closing the popup, we handle this ourselves. - putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP, Boolean.TRUE); + setFocusable(false); } /** @@ -162,28 +163,22 @@ public class BasicArrowButton extends JButton implements SwingConstants public void paint(Graphics g) { super.paint(g); - Rectangle bounds = getBounds(); - int size = bounds.height / 4; - int x = bounds.x + (bounds.width - size) / 2; - int y = (bounds.height - size) / 4; + + int height = getHeight(); + int size = height / 4; + + int x = (getWidth() - size) / 2; + int y = (height - size) / 2; + ButtonModel m = getModel(); if (m.isArmed()) { x++; y++; } + paintTriangle(g, x, y, size, direction, isEnabled()); } - - /** The preferred size for the button. */ - private static final Dimension PREFERRED_SIZE = new Dimension(16, 16); - - /** The minimum size for the button. */ - private static final Dimension MINIMUM_SIZE = new Dimension(5, 5); - - /** The maximum size for the button. */ - private static final Dimension MAXIMUM_SIZE - = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); /** * Returns the preferred size of the arrow button. @@ -192,7 +187,10 @@ public class BasicArrowButton extends JButton implements SwingConstants */ public Dimension getPreferredSize() { - return PREFERRED_SIZE; + // since Dimension is NOT immutable, we must return a new instance + // every time (if we return a cached value, the caller might modify it) + // - tests show that the reference implementation does the same. + return new Dimension(16, 16); } /** @@ -202,17 +200,23 @@ public class BasicArrowButton extends JButton implements SwingConstants */ public Dimension getMinimumSize() { - return MINIMUM_SIZE; + // since Dimension is NOT immutable, we must return a new instance + // every time (if we return a cached value, the caller might modify it) + // - tests show that the reference implementation does the same. + return new Dimension(5, 5); } /** * Returns the maximum size of the arrow button. * - * @return The maximum size. + * @return The maximum size (always Integer.MAX_VALUE x Integer.MAX_VALUE). */ public Dimension getMaximumSize() { - return MAXIMUM_SIZE; + // since Dimension is NOT immutable, we must return a new instance + // every time (if we return a cached value, the caller might modify it) + // - tests show that the reference implementation does the same. + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java index 89e99a29a31..84895821518 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java @@ -38,13 +38,17 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.SystemProperties; + import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -66,7 +70,23 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, public void propertyChange(PropertyChangeEvent e) { - // TODO: What should be done here, if anything? + // Store the TextLayout for this in a client property for speed-up + // painting of the label. + String property = e.getPropertyName(); + if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY) + || property.equals("font")) + && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") + == null) + { + AbstractButton b = (AbstractButton) e.getSource(); + String text = b.getText(); + if (text == null) + text = ""; + FontRenderContext frc = new FontRenderContext(new AffineTransform(), + false, false); + TextLayout layout = new TextLayout(text, b.getFont(), frc); + b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout); + } } protected void checkOpacity(AbstractButton b) @@ -160,11 +180,14 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, { AbstractButton button = (AbstractButton) e.getSource(); ButtonModel model = button.getModel(); - if (e.getButton() == MouseEvent.BUTTON1) + if (SwingUtilities.isLeftMouseButton(e)) { // It is important that these transitions happen in this order. model.setArmed(true); model.setPressed(true); + + if (! button.isFocusOwner() && button.isRequestFocusEnabled()) + button.requestFocus(); } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java index 0a537c4bdd8..d531133ba26 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java @@ -442,13 +442,17 @@ public class BasicButtonUI extends ButtonUI if (b.isEnabled()) { g.setColor(b.getForeground()); - g.drawString(text, textRect.x, textRect.y + fm.getAscent()); + // FIXME: Underline mnemonic. + BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x, + textRect.y + fm.getAscent()); } else { String prefix = getPropertyPrefix(); g.setColor(UIManager.getColor(prefix + "disabledText")); - g.drawString(text, textRect.x, textRect.y + fm.getAscent()); + // FIXME: Underline mnemonic. + BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x, + textRect.y + fm.getAscent()); } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java index 2cb1623cbc2..d98fd2afe38 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -62,6 +60,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; import javax.swing.CellRendererPane; import javax.swing.ComboBoxEditor; import javax.swing.ComboBoxModel; @@ -241,6 +240,8 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.setLayout(createLayoutManager()); comboBox.setFocusable(true); installKeyboardActions(); + comboBox.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP, + Boolean.TRUE); } } @@ -288,7 +289,7 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.addPropertyChangeListener(propertyChangeListener); focusListener = createFocusListener(); - editor.addFocusListener(focusListener); + comboBox.addFocusListener(focusListener); itemListener = createItemListener(); comboBox.addItemListener(itemListener); @@ -542,7 +543,9 @@ public class BasicComboBoxUI extends ComboBoxUI { editor.setFont(comboBox.getFont()); if (popupKeyListener != null) - editor.addKeyListener(popupKeyListener); + editor.addKeyListener(popupKeyListener); + if (keyListener != null) + editor.addKeyListener(keyListener); comboBox.configureEditor(comboBox.getEditor(), comboBox.getSelectedItem()); } @@ -554,6 +557,8 @@ public class BasicComboBoxUI extends ComboBoxUI { if (popupKeyListener != null) editor.removeKeyListener(popupKeyListener); + if (keyListener != null) + editor.removeKeyListener(keyListener); } /** @@ -571,6 +576,10 @@ public class BasicComboBoxUI extends ComboBoxUI arrowButton.addMouseListener(popupMouseListener); if (popupMouseMotionListener != null) arrowButton.addMouseMotionListener(popupMouseMotionListener); + + // Mark the button as not closing the popup, we handle this ourselves. + arrowButton.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP, + Boolean.TRUE); } } @@ -712,18 +721,51 @@ public class BasicComboBoxUI extends ComboBoxUI return new Dimension(32767, 32767); } + /** + * Returns the number of accessible children of the combobox. + * + * @param c the component (combobox) to check, ignored + * + * @return the number of accessible children of the combobox + */ public int getAccessibleChildrenCount(JComponent c) - throws NotImplementedException { - // FIXME: Need to implement - return 0; + int count = 1; + if (comboBox.isEditable()) + count = 2; + return count; } + /** + * Returns the accessible child with the specified index. + * + * @param c the component, this is ignored + * @param i the index of the accessible child to return + */ public Accessible getAccessibleChild(JComponent c, int i) - throws NotImplementedException { - // FIXME: Need to implement - return null; + Accessible child = null; + switch (i) + { + case 0: // The popup. + if (popup instanceof Accessible) + { + AccessibleContext ctx = ((Accessible) popup).getAccessibleContext(); + ctx.setAccessibleParent(comboBox); + child = (Accessible) popup; + } + break; + case 1: // The editor, if any. + if (comboBox.isEditable() && editor instanceof Accessible) + { + AccessibleContext ctx = + ((Accessible) editor).getAccessibleContext(); + ctx.setAccessibleParent(comboBox); + child = (Accessible) editor; + } + break; + } + return child; } /** @@ -735,10 +777,11 @@ public class BasicComboBoxUI extends ComboBoxUI * @return true if the specified key is a navigation key and false otherwis */ protected boolean isNavigationKey(int keyCode) - throws NotImplementedException { - // FIXME: Need to implement - return false; + return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN + || keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT + || keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_ESCAPE + || keyCode == KeyEvent.VK_TAB; } /** @@ -759,7 +802,7 @@ public class BasicComboBoxUI extends ComboBoxUI protected void selectPreviousPossibleValue() { int index = comboBox.getSelectedIndex(); - if (index != 0) + if (index > 0) comboBox.setSelectedIndex(index - 1); } @@ -1163,10 +1206,31 @@ public class BasicComboBoxUI extends ComboBoxUI * Invoked whenever key is pressed while JComboBox is in focus. */ public void keyPressed(KeyEvent e) - throws NotImplementedException { - // FIXME: This method calls JComboBox.selectWithKeyChar if the key that - // was pressed is not a navigation key. + if (comboBox.getModel().getSize() != 0 && comboBox.isEnabled()) + { + if (! isNavigationKey(e.getKeyCode())) + { + if (! comboBox.isEditable()) + if (comboBox.selectWithKeyChar(e.getKeyChar())) + e.consume(); + } + else + { + if (e.getKeyCode() == KeyEvent.VK_UP && comboBox.isPopupVisible()) + selectPreviousPossibleValue(); + else if (e.getKeyCode() == KeyEvent.VK_DOWN) + { + if (comboBox.isPopupVisible()) + selectNextPossibleValue(); + else + comboBox.showPopup(); + } + else if (e.getKeyCode() == KeyEvent.VK_ENTER + || e.getKeyCode() == KeyEvent.VK_ESCAPE) + popup.hide(); + } + } } } @@ -1330,5 +1394,4 @@ public class BasicComboBoxUI extends ComboBoxUI // FIXME: Need to handle changes in other bound properties. } } - } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java b/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java index ef7a880c2ac..ed916cb5f1a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java @@ -1,5 +1,5 @@ /* BasicDirectoryModel.java -- - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,35 +40,296 @@ package javax.swing.plaf.basic; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; import java.util.Vector; import javax.swing.AbstractListModel; import javax.swing.JFileChooser; +import javax.swing.SwingUtilities; import javax.swing.event.ListDataEvent; import javax.swing.filechooser.FileSystemView; /** - * DOCUMENT ME! + * Implements an AbstractListModel for directories where the source + * of the files is a JFileChooser object. + * + * This class is used for sorting and ordering the file list in + * a JFileChooser L&F object. */ public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener { - /** DOCUMENT ME! */ + /** The list of files itself */ private Vector contents; - /** DOCUMENT ME! */ - private int directories; + /** + * The directories in the list. + */ + private Vector directories; + + /** + * The files in the list. + */ + private Vector files; - /** DOCUMENT ME! */ + /** The listing mode of the associated JFileChooser, + either FILES_ONLY, DIRECTORIES_ONLY or FILES_AND_DIRECTORIES */ private int listingMode; - /** DOCUMENT ME! */ + /** The JFileCooser associated with this model */ private JFileChooser filechooser; - /** DOCUMENT ME! */ + /** + * The thread that loads the file view. + */ + private DirectoryLoadThread loadThread; + + /** + * This thread is responsible for loading file lists from the + * current directory and updating the model. + */ + private class DirectoryLoadThread extends Thread + { + + /** + * Updates the Swing list model. + */ + private class UpdateSwingRequest + implements Runnable + { + + private List added; + private int addIndex; + private List removed; + private int removeIndex; + private boolean cancel; + + UpdateSwingRequest(List add, int ai, List rem, int ri) + { + added = add; + addIndex = ai; + removed = rem; + removeIndex = ri; + cancel = false; + } + + public void run() + { + if (! cancel) + { + int numRemoved = removed == null ? 0 : removed.size(); + int numAdded = added == null ? 0 : added.size(); + synchronized (contents) + { + if (numRemoved > 0) + contents.removeAll(removed); + if (numAdded > 0) + contents.addAll(added); + + files = null; + directories = null; + } + if (numRemoved > 0 && numAdded == 0) + fireIntervalRemoved(BasicDirectoryModel.this, removeIndex, + removeIndex + numRemoved - 1); + else if (numRemoved == 0 && numAdded > 0) + fireIntervalAdded(BasicDirectoryModel.this, addIndex, + addIndex + numAdded - 1); + else + fireContentsChanged(); + } + } + + void cancel() + { + cancel = true; + } + } + + /** + * The directory beeing loaded. + */ + File directory; + + /** + * Stores all UpdateSwingRequests that are sent to the event queue. + */ + private UpdateSwingRequest pending; + + /** + * Creates a new DirectoryLoadThread that loads the specified + * directory. + * + * @param dir the directory to load + */ + DirectoryLoadThread(File dir) + { + super("Basic L&F directory loader"); + directory = dir; + } + + public void run() + { + FileSystemView fsv = filechooser.getFileSystemView(); + File[] files = fsv.getFiles(directory, + filechooser.isFileHidingEnabled()); + + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + + // Check list for accepted files. + Vector accepted = new Vector(); + for (int i = 0; i < files.length; i++) + { + if (filechooser.accept(files[i])) + accepted.add(files[i]); + } + + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + + // Sort list. + sort(accepted); + + // Now split up directories from files so that we get the directories + // listed before the files. + Vector newFiles = new Vector(); + Vector newDirectories = new Vector(); + for (Iterator i = accepted.iterator(); i.hasNext();) + { + File f = (File) i.next(); + boolean traversable = filechooser.isTraversable(f); + if (traversable) + newDirectories.add(f); + else if (! traversable && filechooser.isFileSelectionEnabled()) + newFiles.add(f); + + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + + } + + // Build up new file cache. Try to update only the changed elements. + // This will be important for actions like adding new files or + // directories inside a large file list. + Vector newCache = new Vector(newDirectories); + newCache.addAll(newFiles); + + int newSize = newCache.size(); + int oldSize = contents.size(); + if (newSize < oldSize) + { + // Check for removed interval. + int start = -1; + int end = -1; + boolean found = false; + for (int i = 0; i < newSize && !found; i++) + { + if (! newCache.get(i).equals(contents.get(i))) + { + start = i; + end = i + oldSize - newSize; + found = true; + } + } + if (start >= 0 && end > start + && contents.subList(end, oldSize) + .equals(newCache.subList(start, newSize))) + { + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + + Vector removed = new Vector(contents.subList(start, end)); + UpdateSwingRequest r = new UpdateSwingRequest(null, 0, + removed, start); + invokeLater(r); + newCache = null; + } + } + else if (newSize > oldSize) + { + // Check for inserted interval. + int start = oldSize; + int end = newSize; + boolean found = false; + for (int i = 0; i < oldSize && ! found; i++) + { + if (! newCache.get(i).equals(contents.get(i))) + { + start = i; + boolean foundEnd = false; + for (int j = i; j < newSize && ! foundEnd; j++) + { + if (newCache.get(j).equals(contents.get(i))) + { + end = j; + foundEnd = true; + } + } + end = i + oldSize - newSize; + } + } + if (start >= 0 && end > start + && newCache.subList(end, newSize) + .equals(contents.subList(start, oldSize))) + { + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + + List added = newCache.subList(start, end); + UpdateSwingRequest r = new UpdateSwingRequest(added, start, + null, 0); + invokeLater(r); + newCache = null; + } + } + + // Handle complete list changes (newCache != null). + if (newCache != null && ! contents.equals(newCache)) + { + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + UpdateSwingRequest r = new UpdateSwingRequest(newCache, 0, + contents, 0); + invokeLater(r); + } + } + + /** + * Wraps SwingUtilities.invokeLater() and stores the request in + * a Vector so that we can still cancel it later. + * + * @param update the request to invoke + */ + private void invokeLater(UpdateSwingRequest update) + { + pending = update; + SwingUtilities.invokeLater(update); + } + + /** + * Cancels all pending update requests that might be in the AWT + * event queue. + */ + void cancelPending() + { + if (pending != null) + pending.cancel(); + } + } + + /** A Comparator class/object for sorting the file list. */ private Comparator comparator = new Comparator() { public int compare(Object o1, Object o2) @@ -91,14 +352,15 @@ public class BasicDirectoryModel extends AbstractListModel filechooser.addPropertyChangeListener(this); listingMode = filechooser.getFileSelectionMode(); contents = new Vector(); + validateFileCache(); } /** - * DOCUMENT ME! + * Returns whether a given (File) object is included in the list. * - * @param o DOCUMENT ME! + * @param o - The file object to test. * - * @return DOCUMENT ME! + * @return <code>true</code> if the list contains the given object. */ public boolean contains(Object o) { @@ -106,7 +368,7 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! + * Fires a content change event. */ public void fireContentsChanged() { @@ -114,80 +376,99 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! + * Returns a Vector of (java.io.File) objects containing + * the directories in this list. * - * @return DOCUMENT ME! + * @return a Vector */ public Vector getDirectories() { - Vector tmp = new Vector(); - for (int i = 0; i < directories; i++) - tmp.add(contents.get(i)); - return tmp; + // Synchronize this with the UpdateSwingRequest for the case when + // contents is modified. + synchronized (contents) + { + Vector dirs = directories; + if (dirs == null) + { + // Initializes this in getFiles(). + getFiles(); + dirs = directories; + } + return dirs; + } } /** - * DOCUMENT ME! + * Returns the (java.io.File) object at + * an index in the list. * - * @param index DOCUMENT ME! - * - * @return DOCUMENT ME! + * @param index The list index + * @return a File object */ public Object getElementAt(int index) { if (index > getSize() - 1) return null; - if (listingMode == JFileChooser.FILES_ONLY) - return contents.get(directories + index); - else - return contents.elementAt(index); + return contents.elementAt(index); } /** - * DOCUMENT ME! + * Returns a Vector of (java.io.File) objects containing + * the files in this list. * - * @return DOCUMENT ME! + * @return a Vector */ public Vector getFiles() { - Vector tmp = new Vector(); - for (int i = directories; i < getSize(); i++) - tmp.add(contents.get(i)); - return tmp; + synchronized (contents) + { + Vector f = files; + if (f == null) + { + f = new Vector(); + Vector d = new Vector(); // Directories; + for (Iterator i = contents.iterator(); i.hasNext();) + { + File file = (File) i.next(); + if (filechooser.isTraversable(file)) + d.add(file); + else + f.add(file); + } + files = f; + directories = d; + } + return f; + } } /** - * DOCUMENT ME! + * Returns the size of the list, which only includes directories + * if the JFileChooser is set to DIRECTORIES_ONLY. + * + * Otherwise, both directories and files are included in the count. * - * @return DOCUMENT ME! + * @return The size of the list. */ public int getSize() { - if (listingMode == JFileChooser.DIRECTORIES_ONLY) - return directories; - else if (listingMode == JFileChooser.FILES_ONLY) - return contents.size() - directories; return contents.size(); } /** - * DOCUMENT ME! + * Returns the index of an (java.io.File) object in the list. * - * @param o DOCUMENT ME! + * @param o The object - normally a File. * - * @return DOCUMENT ME! + * @return the index of that object, or -1 if it is not in the list. */ public int indexOf(Object o) { - if (listingMode == JFileChooser.FILES_ONLY) - return contents.indexOf(o) - directories; return contents.indexOf(o); } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Obsoleted method which does nothing. */ public void intervalAdded(ListDataEvent e) { @@ -195,9 +476,7 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Obsoleted method which does nothing. */ public void intervalRemoved(ListDataEvent e) { @@ -205,7 +484,7 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! + * Obsoleted method which does nothing. */ public void invalidateFileCache() { @@ -213,12 +492,16 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! + * Less than, determine the relative order in the list of two files + * for sorting purposes. + * + * The order is: directories < files, and thereafter alphabetically, + * using the default locale collation. * - * @param a DOCUMENT ME! - * @param b DOCUMENT ME! + * @param a the first file + * @param b the second file * - * @return DOCUMENT ME! + * @return <code>true</code> if a > b, <code>false</code> if a < b. */ protected boolean lt(File a, File b) { @@ -241,73 +524,66 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! + * Listens for a property change; the change in file selection mode of the + * associated JFileChooser. Reloads the file cache on that event. * - * @param e DOCUMENT ME! + * @param e - A PropertyChangeEvent. */ public void propertyChange(PropertyChangeEvent e) { - if (e.getPropertyName().equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) - listingMode = filechooser.getFileSelectionMode(); + String property = e.getPropertyName(); + if (property.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) + || property.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY) + || property.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY) + || property.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) + || property.equals(JFileChooser.FILE_VIEW_CHANGED_PROPERTY) + ) + { + validateFileCache(); + } } /** - * DOCUMENT ME! + * Renames a file - However, does <I>not</I> re-sort the list + * or replace the old file with the new one in the list. * - * @param oldFile DOCUMENT ME! - * @param newFile DOCUMENT ME! + * @param oldFile The old file + * @param newFile The new file name * - * @return DOCUMENT ME! + * @return <code>true</code> if the rename succeeded */ public boolean renameFile(File oldFile, File newFile) { - // FIXME: implement - return false; + return oldFile.renameTo( newFile ); } /** - * DOCUMENT ME! + * Sorts a Vector of File objects. * - * @param v DOCUMENT ME! + * @param v The Vector to sort. */ protected void sort(Vector v) { Collections.sort(v, comparator); - Enumeration e = Collections.enumeration(v); - Vector tmp = new Vector(); - for (; e.hasMoreElements();) - tmp.add(e.nextElement()); - - contents = tmp; } /** - * DOCUMENT ME! + * Re-loads the list of files */ public void validateFileCache() { - contents.clear(); - directories = 0; - FileSystemView fsv = filechooser.getFileSystemView(); - File[] list = fsv.getFiles(filechooser.getCurrentDirectory(), - filechooser.isFileHidingEnabled()); - - if (list == null) - return; - - for (int i = 0; i < list.length; i++) + File dir = filechooser.getCurrentDirectory(); + if (dir != null) { - if (list[i] == null) - continue; - if (filechooser.accept(list[i])) - { - contents.add(list[i]); - if (filechooser.isTraversable(list[i])) - directories++; - } + // Cancel all pending requests. + if (loadThread != null) + { + loadThread.interrupt(); + loadThread.cancelPending(); + } + loadThread = new DirectoryLoadThread(dir); + loadThread.start(); } - sort(contents); - filechooser.revalidate(); - filechooser.repaint(); } } + diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java index 1356db4aeec..dc1c051225c 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java @@ -160,6 +160,8 @@ public class BasicFileChooserUI extends FileChooserUI else { File f = new File(filechooser.getCurrentDirectory(), getFileName()); + if ( selectedDir != null ) + f = selectedDir; if (filechooser.isTraversable(f)) { filechooser.setCurrentDirectory(f); @@ -266,7 +268,14 @@ public class BasicFileChooserUI extends FileChooserUI */ public String getName(File f) { - return f.getName(); + String name = null; + if (f != null) + { + JFileChooser c = getFileChooser(); + FileSystemView v = c.getFileSystemView(); + name = v.getSystemDisplayName(f); + } + return name; } /** @@ -409,7 +418,7 @@ public class BasicFileChooserUI extends FileChooserUI closeDialog(); } } - else + else // single click { String path = p.toString(); File f = fsv.createFileObject(path); @@ -436,10 +445,11 @@ public class BasicFileChooserUI extends FileChooserUI } lastSelected = path; parentPath = path.substring(0, path.lastIndexOf("/") + 1); + if (f.isFile()) setFileName(path.substring(path.lastIndexOf("/") + 1)); - else if (filechooser.getFileSelectionMode() == - JFileChooser.DIRECTORIES_ONLY) + else if (filechooser.getFileSelectionMode() != + JFileChooser.FILES_ONLY) setFileName(path); } } @@ -538,7 +548,7 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Sets the JFileChooser to the selected file on an update * * @param e DOCUMENT ME! */ @@ -550,9 +560,15 @@ public class BasicFileChooserUI extends FileChooserUI return; File file = filechooser.getFileSystemView().createFileObject(f.toString()); if (! filechooser.isTraversable(file)) - filechooser.setSelectedFile(file); + { + selectedDir = null; + filechooser.setSelectedFile(file); + } else - filechooser.setSelectedFile(null); + { + selectedDir = file; + filechooser.setSelectedFile(null); + } } } @@ -752,6 +768,13 @@ public class BasicFileChooserUI extends FileChooserUI * @see #getUpdateAction() */ private UpdateAction updateAction; + + /** + * When in FILES_ONLY, mode a directory cannot be selected, so + * we save a reference to any it here. This is used to enter + * the directory on "Open" when in that mode. + */ + private File selectedDir; // -- end private -- @@ -874,7 +897,9 @@ public class BasicFileChooserUI extends FileChooserUI protected void installListeners(JFileChooser fc) { propertyChangeListener = createPropertyChangeListener(filechooser); - filechooser.addPropertyChangeListener(propertyChangeListener); + if (propertyChangeListener != null) + filechooser.addPropertyChangeListener(propertyChangeListener); + fc.addPropertyChangeListener(getModel()); } /** @@ -884,8 +909,12 @@ public class BasicFileChooserUI extends FileChooserUI */ protected void uninstallListeners(JFileChooser fc) { - filechooser.removePropertyChangeListener(propertyChangeListener); - propertyChangeListener = null; + if (propertyChangeListener != null) + { + filechooser.removePropertyChangeListener(propertyChangeListener); + propertyChangeListener = null; + } + fc.removePropertyChangeListener(getModel()); } /** @@ -1046,12 +1075,8 @@ public class BasicFileChooserUI extends FileChooserUI */ public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { - return new PropertyChangeListener() - { - public void propertyChange(PropertyChangeEvent e) - { - } - }; + // The RI returns null here, so do we. + return null; } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java index 068de345bec..1e84be93282 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java @@ -37,6 +37,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.SystemProperties; + import java.awt.Color; import java.awt.Dimension; import java.awt.Font; @@ -65,6 +67,14 @@ import javax.swing.SwingUtilities; public class BasicGraphicsUtils { /** + * Used as a key for a client property to store cached TextLayouts in. This + * is used for speed-up drawing of text in + * {@link #drawString(Graphics, String, int, int, int)}. + */ + static final String CACHED_TEXT_LAYOUT = + "BasicGraphicsUtils.cachedTextLayout"; + + /** * Constructor. It is utterly unclear why this class should * be constructable, but this is what the API specification * says. @@ -536,6 +546,170 @@ public class BasicGraphicsUtils g2.fill(underline); } + /** + * Draws a string on the specified component. + * + * @param c the component + * @param g the Graphics context + * @param text the string + * @param underlinedChar the character to be underlined + * @param x the X location + * @param y the Y location + */ + static void drawString(JComponent c, Graphics g, String text, + int underlinedChar, int x, int y) + { + int index = -1; + + /* It is intentional that lower case is used. In some languages, + * the set of lowercase characters is larger than the set of + * uppercase ones. Therefore, it is good practice to use lowercase + * for such comparisons (which really means that the author of this + * code can vaguely remember having read some Unicode techreport + * with this recommendation, but is too lazy to look for the URL). + */ + if ((underlinedChar >= 0) || (underlinedChar <= 0xffff)) + index = text.toLowerCase().indexOf( + Character.toLowerCase((char) underlinedChar)); + + drawStringUnderlineCharAt(c, g, text, index, x, y); + } + + + /** + * Draws a String at the given location, underlining the character + * at the specified index. Drawing is performed in the current color + * and font of <code>g</code>. + * + * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500" + * height="100" alt="[An illustration showing how to use the + * method]" /> + * + * This is an accelerated version of the method with the same name. It + * uses a pre-laid out TextLayout stored in a client property. + * + * @param c the component that is drawn + * @param g the graphics into which the String is drawn. + * + * @param text the String to draw. + * + * @param underlinedIndex the index of the underlined character in + * <code>text</code>. If <code>underlinedIndex</code> falls + * outside the range <code>[0, text.length() - 1]</code>, the + * text will be drawn without underlining anything. + * + * @param x the x coordinate of the text, as it would be passed to + * {@link java.awt.Graphics#drawString(java.lang.String, + * int, int)}. + * + * @param y the y coordinate of the text, as it would be passed to + * {@link java.awt.Graphics#drawString(java.lang.String, + * int, int)}. + */ + static void drawStringUnderlineCharAt(JComponent c, Graphics g, String text, + int underlinedIndex, + int x, int y) + { + Graphics2D g2; + Rectangle2D.Double underline; + FontRenderContext frc; + FontMetrics fmet; + LineMetrics lineMetrics; + Font font; + TextLayout layout; + double underlineX1, underlineX2; + boolean drawUnderline; + int textLength; + + textLength = text.length(); + if (textLength == 0) + return; + + drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength); + + // FIXME: unfortunately pango and cairo can't agree on metrics + // so for the time being we continue to *not* use TextLayouts. + if (!(g instanceof Graphics2D) + || SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") != null) + { + /* Fall-back. This is likely to produce garbage for any text + * containing right-to-left (Hebrew or Arabic) characters, even + * if the underlined character is left-to-right. + */ + g.drawString(text, x, y); + if (drawUnderline) + { + fmet = g.getFontMetrics(); + g.fillRect( + /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)), + /* y */ y + fmet.getDescent() - 1, + /* width */ fmet.charWidth(text.charAt(underlinedIndex)), + /* height */ 1); + } + + return; + } + + g2 = (Graphics2D) g; + font = g2.getFont(); + frc = g2.getFontRenderContext(); + lineMetrics = font.getLineMetrics(text, frc); + layout = (TextLayout) c.getClientProperty(CACHED_TEXT_LAYOUT); + if (layout == null) + { + layout = new TextLayout(text, font, frc); + System.err.println("Unable to use cached TextLayout for: " + text); + } + + /* Draw the text. */ + layout.draw(g2, x, y); + if (!drawUnderline) + return; + + underlineX1 = x + layout.getLogicalHighlightShape( + underlinedIndex, underlinedIndex).getBounds2D().getX(); + underlineX2 = x + layout.getLogicalHighlightShape( + underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX(); + + underline = new Rectangle2D.Double(); + if (underlineX1 < underlineX2) + { + underline.x = underlineX1; + underline.width = underlineX2 - underlineX1; + } + else + { + underline.x = underlineX2; + underline.width = underlineX1 - underlineX2; + } + + + underline.height = lineMetrics.getUnderlineThickness(); + underline.y = lineMetrics.getUnderlineOffset(); + if (underline.y == 0) + { + /* Some fonts do not specify an underline offset, although they + * actually should do so. In that case, the result of calling + * lineMetrics.getUnderlineOffset() will be zero. Since it would + * look very ugly if the underline was be positioned immediately + * below the baseline, we check for this and move the underline + * below the descent, as shown in the following ASCII picture: + * + * ##### ##### # + * # # # # + * # # # # + * # # # # + * ##### ###### ---- baseline (0) + * # + * # + * ------------------###----------- lineMetrics.getDescent() + */ + underline.y = lineMetrics.getDescent(); + } + + underline.y += y; + g2.fill(underline); + } /** * Draws a rectangle, simulating a dotted stroke by painting only diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java index 23bcdc315ee..8f2181336cb 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.AWTEvent; import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -692,17 +691,12 @@ public class BasicInternalFrameUI extends InternalFrameUI /** The MouseEvent target. */ private transient Component mouseEventTarget; - /** The component pressed. */ - private transient Component pressedComponent; + private Component dragTarget; - /** The last component entered. */ - private transient Component lastComponentEntered; - - /** Used to store/reset lastComponentEntered. */ - private transient Component tempComponent; - - /** The number of presses. */ - private transient int pressCount; + /** + * Indicates if we are currently in a dragging operation or not. + */ + private boolean isDragging; /** * This method is called when the mouse enters the glass pane. @@ -767,7 +761,10 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public void mousePressed(MouseEvent e) { - activateFrame(frame); + // Experiments show that this seems to call the + // borderListener.mousePressed() method to activate the frame. + if (borderListener != null) + borderListener.mousePressed(e); handleEvent(e); } @@ -783,149 +780,104 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** - * This method acquires a candidate component to dispatch the MouseEvent to. + * This is a helper method that dispatches the GlassPane MouseEvents to the + * proper component. * - * @param me - * The MouseEvent to acquire a component for. + * @param e the mouse event to be dispatched */ - private void acquireComponentForMouseEvent(MouseEvent me) + private void handleEvent(MouseEvent e) { - int x = me.getX(); - int y = me.getY(); - - // Find the candidate which should receive this event. - Component parent = frame.getLayeredPane(); - if (parent == null) - return; - Component candidate = null; - Point p = me.getPoint(); - while (candidate == null && parent != null) - { - candidate = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); - if (candidate == null) - { - p = SwingUtilities.convertPoint(parent, p.x, p.y, - parent.getParent()); - parent = parent.getParent(); - } - } - - // If the only candidate we found was the native container itself, - // don't dispatch any event at all. We only care about the lightweight - // children here. - if (candidate == frame.getContentPane()) - candidate = null; - - // If our candidate is new, inform the old target we're leaving. - if (lastComponentEntered != null && lastComponentEntered.isShowing() - && lastComponentEntered != candidate) + // Find candidate component inside the JInternalFrame. + Component target = frame.getLayeredPane().findComponentAt(e.getX(), + e.getY()); + + // Now search upwards to find a component that actually has + // a MouseListener attached. + while (target != null + && target.getMouseListeners().length == 0 + && target.getMouseMotionListeners().length == 0 + && target.getMouseWheelListeners().length == 0) { - Point tp = SwingUtilities.convertPoint(frame.getContentPane(), x, y, - lastComponentEntered); - MouseEvent exited = new MouseEvent(lastComponentEntered, - MouseEvent.MOUSE_EXITED, - me.getWhen(), me.getModifiersEx(), - tp.x, tp.y, me.getClickCount(), - me.isPopupTrigger(), - me.getButton()); - tempComponent = lastComponentEntered; - lastComponentEntered = null; - tempComponent.dispatchEvent(exited); + target = target.getParent(); } - // If we have a candidate, maybe enter it. - if (candidate != null) + if (target != null) { - mouseEventTarget = candidate; - if (candidate.isLightweight() && candidate.isShowing() - && candidate != frame.getContentPane() - && candidate != lastComponentEntered) - { - lastComponentEntered = mouseEventTarget; - Point cp = SwingUtilities.convertPoint(frame.getContentPane(), x, - y, lastComponentEntered); - MouseEvent entered = new MouseEvent(lastComponentEntered, - MouseEvent.MOUSE_ENTERED, - me.getWhen(), - me.getModifiersEx(), cp.x, - cp.y, me.getClickCount(), - me.isPopupTrigger(), - me.getButton()); - lastComponentEntered.dispatchEvent(entered); - } - } - - if (me.getID() == MouseEvent.MOUSE_RELEASED - || me.getID() == MouseEvent.MOUSE_PRESSED && pressCount > 0 - || me.getID() == MouseEvent.MOUSE_DRAGGED) - // If any of the following events occur while a button is held down, - // they should be dispatched to the same component to which the - // original MOUSE_PRESSED event was dispatched: - // - MOUSE_RELEASED - // - MOUSE_PRESSED: another button pressed while the first is held down - // - MOUSE_DRAGGED - mouseEventTarget = pressedComponent; - else if (me.getID() == MouseEvent.MOUSE_CLICKED) - { - // Don't dispatch CLICKED events whose target is not the same as the - // target for the original PRESSED event. - if (candidate != pressedComponent) - mouseEventTarget = null; - else if (pressCount == 0) - pressedComponent = null; + int id = e.getID(); + switch (id) + { + case MouseEvent.MOUSE_ENTERED: + // Now redispatch the thing. + if (! isDragging || frame.isSelected()) + { + mouseEventTarget = target; + redispatch(id, e, mouseEventTarget); + } + break; + case MouseEvent.MOUSE_EXITED: + if (! isDragging || frame.isSelected()) + { + redispatch(id, e, mouseEventTarget); + } + break; + case MouseEvent.MOUSE_PRESSED: + mouseEventTarget = target; + redispatch(id, e, mouseEventTarget); + // Start dragging. + dragTarget = target; + break; + case MouseEvent.MOUSE_RELEASED: + if (isDragging) + { + redispatch(id, e, dragTarget); + isDragging = false; + } + else + redispatch(id, e, mouseEventTarget); + break; + case MouseEvent.MOUSE_CLICKED: + redispatch(id, e, mouseEventTarget); + break; + case MouseEvent.MOUSE_MOVED: + if (target != mouseEventTarget) + { + // Create additional MOUSE_EXITED/MOUSE_ENTERED pairs. + redispatch(MouseEvent.MOUSE_EXITED, e, mouseEventTarget); + mouseEventTarget = target; + redispatch(MouseEvent.MOUSE_ENTERED, e, mouseEventTarget); + } + redispatch(id, e, mouseEventTarget); + break; + case MouseEvent.MOUSE_DRAGGED: + if (! isDragging) + isDragging = true; + redispatch(id, e, mouseEventTarget); + break; + case MouseEvent.MOUSE_WHEEL: + redispatch(id, e, mouseEventTarget); + break; + default: + assert false : "Must not reach here"; + } } } /** - * This is a helper method that dispatches the GlassPane MouseEvents to the - * proper component. - * - * @param e - * The AWTEvent to be dispatched. Usually an instance of - * MouseEvent. + * Redispatches the event to the real target with the specified id. + * + * @param id the new event ID + * @param e the original event + * @param target the real event target */ - private void handleEvent(AWTEvent e) + private void redispatch(int id, MouseEvent e, Component target) { - if (e instanceof MouseEvent) - { - MouseEvent me = (MouseEvent) e; - acquireComponentForMouseEvent(me); - - //If there is no target, return - if (mouseEventTarget == null) - return; - - //Avoid re-dispatching to ourselves and causing an infinite loop - if (mouseEventTarget.equals(frame.getGlassPane())) - return; - - // Avoid dispatching ENTERED and EXITED events twice. - if (mouseEventTarget.isShowing() - && e.getID() != MouseEvent.MOUSE_ENTERED - && e.getID() != MouseEvent.MOUSE_EXITED) - { - MouseEvent newEvt = SwingUtilities.convertMouseEvent( - frame.getGlassPane(), - me, - mouseEventTarget); - mouseEventTarget.dispatchEvent(newEvt); - - switch (e.getID()) - { - case MouseEvent.MOUSE_PRESSED: - if (pressCount++ == 0) - pressedComponent = mouseEventTarget; - break; - case MouseEvent.MOUSE_RELEASED: - // Clear our memory of the original PRESSED event, only if - // we're not expecting a CLICKED event after this. If - // there is a CLICKED event after this, it will do clean up. - if (--pressCount == 0 && mouseEventTarget != pressedComponent) - pressedComponent = null; - break; - } - } - } + Point p = SwingUtilities.convertPoint(frame.getLayeredPane(), e.getX(), + e.getY(), target); + MouseEvent ev = new MouseEvent(target, id, e.getWhen(), + e.getModifiers() | e.getModifiersEx(), + p.x, p.y, e.getClickCount(), + e.isPopupTrigger()); + target.dispatchEvent(ev); } } @@ -945,41 +897,61 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(JInternalFrame.IS_MAXIMUM_PROPERTY)) + String property = evt.getPropertyName(); + if (property.equals(JInternalFrame.IS_MAXIMUM_PROPERTY)) { if (frame.isMaximum()) maximizeFrame(frame); else minimizeFrame(frame); } - else if (evt.getPropertyName().equals(JInternalFrame.IS_ICON_PROPERTY)) + else if (property.equals(JInternalFrame.IS_ICON_PROPERTY)) { if (frame.isIcon()) iconifyFrame(frame); else deiconifyFrame(frame); } - else if (evt.getPropertyName().equals(JInternalFrame.IS_SELECTED_PROPERTY)) + else if (property.equals(JInternalFrame.IS_SELECTED_PROPERTY)) { + Component glassPane = frame.getGlassPane(); if (frame.isSelected()) - activateFrame(frame); + { + activateFrame(frame); + glassPane.setVisible(false); + } else - deactivateFrame(frame); + { + deactivateFrame(frame); + glassPane.setVisible(true); + } } - else if (evt.getPropertyName().equals(JInternalFrame.ROOT_PANE_PROPERTY) - || evt.getPropertyName().equals( - JInternalFrame.GLASS_PANE_PROPERTY)) + else if (property.equals(JInternalFrame.ROOT_PANE_PROPERTY) + || property.equals(JInternalFrame.GLASS_PANE_PROPERTY)) { Component old = (Component) evt.getOldValue(); - old.removeMouseListener(glassPaneDispatcher); - old.removeMouseMotionListener(glassPaneDispatcher); + if (old != null) + { + old.removeMouseListener(glassPaneDispatcher); + old.removeMouseMotionListener(glassPaneDispatcher); + } Component newPane = (Component) evt.getNewValue(); - newPane.addMouseListener(glassPaneDispatcher); - newPane.addMouseMotionListener(glassPaneDispatcher); + if (newPane != null) + { + newPane.addMouseListener(glassPaneDispatcher); + newPane.addMouseMotionListener(glassPaneDispatcher); + } frame.revalidate(); } + else if (property.equals(JInternalFrame.IS_CLOSED_PROPERTY)) + { + if (evt.getNewValue() == Boolean.TRUE) + { + closeFrame(frame); + } + } /* * FIXME: need to add ancestor properties to JComponents. else if * (evt.getPropertyName().equals(JComponent.ANCESTOR_PROPERTY)) { if diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java index 60e3a98684f..304e13ad735 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java @@ -1,5 +1,5 @@ /* BasicLabelUI.java - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,20 +37,25 @@ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Color; +import java.awt.Component; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; import javax.swing.Icon; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.plaf.ComponentUI; @@ -369,14 +374,39 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener } /** - * This method installs the keyboard actions for the given {@link JLabel}. + * Installs the keyboard actions for the given {@link JLabel}. * * @param l The {@link JLabel} to install keyboard actions for. */ protected void installKeyboardActions(JLabel l) - throws NotImplementedException { - //FIXME: implement. + Component c = l.getLabelFor(); + if (c != null) + { + int mnemonic = l.getDisplayedMnemonic(); + if (mnemonic > 0) + { + // add a keystroke for the given mnemonic mapping to 'press'; + InputMap keyMap = new InputMap(); + keyMap.put(KeyStroke.getKeyStroke(mnemonic, KeyEvent.VK_ALT), + "press"); + SwingUtilities.replaceUIInputMap(l, + JComponent.WHEN_IN_FOCUSED_WINDOW, keyMap); + + // add an action to focus the component when 'press' happens + ActionMap map = new ActionMap(); + map.put("press", new AbstractAction() { + public void actionPerformed(ActionEvent event) + { + JLabel label = (JLabel) event.getSource(); + Component c = label.getLabelFor(); + if (c != null) + c.requestFocus(); + } + }); + SwingUtilities.replaceUIActionMap(l, map); + } + } } /** @@ -385,9 +415,10 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener * @param l The {@link JLabel} to uninstall keyboard actions for. */ protected void uninstallKeyboardActions(JLabel l) - throws NotImplementedException { - //FIXME: implement. + SwingUtilities.replaceUIActionMap(l, null); + SwingUtilities.replaceUIInputMap(l, JComponent.WHEN_IN_FOCUSED_WINDOW, + null); } /** @@ -426,5 +457,30 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener JLabel l = (JLabel) e.getSource(); BasicHTML.updateRenderer(l, text); } + else if (e.getPropertyName().equals("displayedMnemonic")) + { + // update the key to action mapping + JLabel label = (JLabel) e.getSource(); + if (label.getLabelFor() != null) + { + int oldMnemonic = ((Integer) e.getOldValue()).intValue(); + int newMnemonic = ((Integer) e.getNewValue()).intValue(); + InputMap keyMap = label.getInputMap( + JComponent.WHEN_IN_FOCUSED_WINDOW); + keyMap.put(KeyStroke.getKeyStroke(oldMnemonic, + KeyEvent.ALT_DOWN_MASK), null); + keyMap.put(KeyStroke.getKeyStroke(newMnemonic, + KeyEvent.ALT_DOWN_MASK), "press"); + } + } + else if (e.getPropertyName().equals("labelFor")) + { + JLabel label = (JLabel) e.getSource(); + InputMap keyMap = label.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + int mnemonic = label.getDisplayedMnemonic(); + if (mnemonic > 0) + keyMap.put(KeyStroke.getKeyStroke(mnemonic, KeyEvent.ALT_DOWN_MASK), + "press"); + } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java index 44f6a408984..493fc0578e3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; @@ -61,12 +59,12 @@ import javax.swing.DefaultListSelectionModel; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JList; -import javax.swing.KeyStroke; import javax.swing.ListCellRenderer; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; +import javax.swing.TransferHandler; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.ListDataEvent; @@ -76,8 +74,8 @@ import javax.swing.event.ListSelectionListener; import javax.swing.event.MouseInputListener; import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.ListUI; +import javax.swing.plaf.UIResource; /** * The Basic Look and Feel UI delegate for the @@ -215,9 +213,26 @@ public class BasicListUI extends ListUI target.actionPerformed(derivedEvent); } } - - class ListAction extends AbstractAction + + /** + * Implements the action for the JList's keyboard commands. + */ + private class ListAction + extends AbstractAction { + // TODO: Maybe make a couple of classes out of this bulk Action. + // Form logical groups of Actions when doing this. + + /** + * Creates a new ListAction for the specified command. + * + * @param cmd the actionCommand to set + */ + ListAction(String cmd) + { + putValue(ACTION_COMMAND_KEY, cmd); + } + public void actionPerformed(ActionEvent e) { int lead = list.getLeadSelectionIndex(); @@ -398,7 +413,7 @@ public class BasicListUI extends ListUI list.ensureIndexIsVisible(list.getLeadSelectionIndex()); } } - + /** * A helper class which listens for {@link MouseEvent}s * from the {@link JList}. @@ -464,7 +479,8 @@ public class BasicListUI extends ListUI */ public void mousePressed(MouseEvent event) { - // TODO: What should be done here, if anything? + // We need to explicitly request focus. + list.requestFocusInWindow(); } /** @@ -992,39 +1008,83 @@ public class BasicListUI extends ListUI */ protected void installKeyboardActions() { + // Install UI InputMap. InputMap focusInputMap = (InputMap) UIManager.get("List.focusInputMap"); - InputMapUIResource parentInputMap = new InputMapUIResource(); - // FIXME: The JDK uses a LazyActionMap for parentActionMap - ActionMap parentActionMap = new ActionMapUIResource(); - action = new ListAction(); - Object keys[] = focusInputMap.allKeys(); - // Register key bindings in the UI InputMap-ActionMap pair - for (int i = 0; i < keys.length; i++) + SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, + focusInputMap); + + // Install UI ActionMap. + ActionMap am = (ActionMap) UIManager.get("List.actionMap"); + if (am == null) { - KeyStroke stroke = (KeyStroke) keys[i]; - String actionString = (String) focusInputMap.get(stroke); - parentInputMap.put(KeyStroke.getKeyStroke(stroke.getKeyCode(), - stroke.getModifiers()), - actionString); - - parentActionMap.put(actionString, - new ActionListenerProxy(action, actionString)); + // Create the actionMap once and store it in the current UIDefaults + // for use in other components. + am = new ActionMapUIResource(); + ListAction action; + action = new ListAction("selectPreviousRow"); + am.put("selectPreviousRow", action); + action = new ListAction("selectNextRow"); + am.put("selectNextRow", action); + action = new ListAction("selectPreviousRowExtendSelection"); + am.put("selectPreviousRowExtendSelection", action); + action = new ListAction("selectNextRowExtendSelection"); + am.put("selectNextRowExtendSelection", action); + + action = new ListAction("selectPreviousColumn"); + am.put("selectPreviousColumn", action); + action = new ListAction("selectNextColumn"); + am.put("selectNextColumn", action); + action = new ListAction("selectPreviousColumnExtendSelection"); + am.put("selectPreviousColumnExtendSelection", action); + action = new ListAction("selectNextColumnExtendSelection"); + am.put("selectNextColumnExtendSelection", action); + + action = new ListAction("selectFirstRow"); + am.put("selectFirstRow", action); + action = new ListAction("selectLastRow"); + am.put("selectLastRow", action); + action = new ListAction("selectFirstRowExtendSelection"); + am.put("selectFirstRowExtendSelection", action); + action = new ListAction("selectLastRowExtendSelection"); + am.put("selectLastRowExtendSelection", action); + + action = new ListAction("scrollUp"); + am.put("scrollUp", action); + action = new ListAction("scrollUpExtendSelection"); + am.put("scrollUpExtendSelection", action); + action = new ListAction("scrollDown"); + am.put("scrollDown", action); + action = new ListAction("scrollDownExtendSelection"); + am.put("scrollDownExtendSelection", action); + + action = new ListAction("selectAll"); + am.put("selectAll", action); + action = new ListAction("clearSelection"); + am.put("clearSelection", action); + + am.put("copy", TransferHandler.getCopyAction()); + am.put("cut", TransferHandler.getCutAction()); + am.put("paste", TransferHandler.getPasteAction()); + + UIManager.put("List.actionMap", am); } - // Register the new InputMap-ActionMap as the parents of the list's - // InputMap and ActionMap - parentInputMap.setParent(list.getInputMap().getParent()); - parentActionMap.setParent(list.getActionMap().getParent()); - list.getInputMap().setParent(parentInputMap); - list.getActionMap().setParent(parentActionMap); + + SwingUtilities.replaceUIActionMap(list, am); } /** * Uninstalls keyboard actions for this UI in the {@link JList}. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // TODO: Implement this properly. + // Uninstall the InputMap. + InputMap im = SwingUtilities.getUIInputMap(list, JComponent.WHEN_FOCUSED); + if (im instanceof UIResource) + SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null); + + // Uninstall the ActionMap. + if (SwingUtilities.getUIActionMap(list) instanceof UIResource) + SwingUtilities.replaceUIActionMap(list, null); } /** @@ -1151,7 +1211,7 @@ public class BasicListUI extends ListUI boolean hasFocus = (list.getLeadSelectionIndex() == row) && BasicListUI.this.list.hasFocus(); Component comp = rend.getListCellRendererComponent(list, data.getElementAt(row), - 0, isSel, hasFocus); + row, isSel, hasFocus); rendererPane.paintComponent(g, comp, list, bounds); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java index 0f6e0243fcf..c056a2403f9 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -64,6 +64,7 @@ import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.BorderFactory; +import javax.swing.JComponent; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.MenuSelectionManager; @@ -129,7 +130,9 @@ public abstract class BasicLookAndFeel extends LookAndFeel if (target instanceof Container) target = ((Container) target).findComponentAt(ev.getPoint()); if (m.getSelectedPath().length > 0 - && ! m.isComponentPartOfCurrentMenu(target)) + && ! m.isComponentPartOfCurrentMenu(target) + && (((JComponent)target).getClientProperty(DONT_CANCEL_POPUP) == null + || !((JComponent)target).getClientProperty(DONT_CANCEL_POPUP).equals(Boolean.TRUE))) { m.clearSelectedPath(); } @@ -1028,6 +1031,25 @@ public abstract class BasicLookAndFeel extends LookAndFeel "PopupMenu.border", new BorderUIResource.BevelBorderUIResource(0), "PopupMenu.font", new FontUIResource("Dialog", Font.PLAIN, 12), "PopupMenu.foreground", new ColorUIResource(darkShadow), + "PopupMenu.selectedWindowInputMapBindings", + new Object[] {"ESCAPE", "cancel", + "DOWN", "selectNext", + "KP_DOWN", "selectNext", + "UP", "selectPrevious", + "KP_UP", "selectPrevious", + "LEFT", "selectParent", + "KP_LEFT", "selectParent", + "RIGHT", "selectChild", + "KP_RIGHT", "selectChild", + "ENTER", "return", + "SPACE", "return" + }, + "PopupMenu.selectedWindowInputMapBindings.RightToLeft", + new Object[] {"LEFT", "selectChild", + "KP_LEFT", "selectChild", + "RIGHT", "selectParent", + "KP_RIGHT", "selectParent", + }, "ProgressBar.background", new ColorUIResource(Color.LIGHT_GRAY), "ProgressBar.border", new BorderUIResource.LineBorderUIResource(Color.GREEN, 2), @@ -1607,7 +1629,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel }), "Tree.font", new FontUIResource("Dialog", Font.PLAIN, 12), "Tree.foreground", new ColorUIResource(Color.black), - "Tree.hash", new ColorUIResource(new Color(128, 128, 128)), + "Tree.hash", new ColorUIResource(new Color(184, 207, 228)), "Tree.leftChildIndent", new Integer(7), "Tree.rightChildIndent", new Integer(13), "Tree.rowHeight", new Integer(16), diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java index f258ebe3069..cd25a3baf77 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java @@ -38,24 +38,31 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Dimension; +import java.awt.event.ActionEvent; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.BoxLayout; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.LookAndFeel; import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.MenuBarUI; @@ -64,6 +71,47 @@ import javax.swing.plaf.MenuBarUI; */ public class BasicMenuBarUI extends MenuBarUI { + + /** + * This action is performed for the action command 'takeFocus'. + */ + private static class FocusAction + extends AbstractAction + { + + /** + * Creates a new FocusAction. + */ + FocusAction() + { + super("takeFocus"); + } + + /** + * Performs the action. + */ + public void actionPerformed(ActionEvent event) + { + // In the JDK this action seems to pop up the first menu of the + // menu bar. + JMenuBar menuBar = (JMenuBar) event.getSource(); + MenuSelectionManager defaultManager = + MenuSelectionManager.defaultManager(); + MenuElement me[]; + MenuElement subElements[]; + JMenu menu = menuBar.getMenu(0); + if (menu != null) + { + me = new MenuElement[3]; + me[0] = (MenuElement) menuBar; + me[1] = (MenuElement) menu; + me[2] = (MenuElement) menu.getPopupMenu(); + defaultManager.setSelectedPath(me); + } + } + + } + protected ChangeListener changeListener; /*ContainerListener that listens to the ContainerEvents fired from menu bar*/ @@ -178,9 +226,46 @@ public class BasicMenuBarUI extends MenuBarUI * This method installs the keyboard actions for the JMenuBar. */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: implement + // Install InputMap. + Object[] bindings = + (Object[]) SharedUIDefaults.get("MenuBar.windowBindings"); + InputMap inputMap = LookAndFeel.makeComponentInputMap(menuBar, bindings); + SwingUtilities.replaceUIInputMap(menuBar, + JComponent.WHEN_IN_FOCUSED_WINDOW, + inputMap); + + // Install ActionMap. + SwingUtilities.replaceUIActionMap(menuBar, getActionMap()); + } + + /** + * Creates and returns the shared action map for JTrees. + * + * @return the shared action map for JTrees + */ + private ActionMap getActionMap() + { + ActionMap am = (ActionMap) UIManager.get("MenuBar.actionMap"); + if (am == null) + { + am = createDefaultActions(); + UIManager.getLookAndFeelDefaults().put("MenuBar.actionMap", am); + } + return am; + } + + /** + * Creates the default actions when there are none specified by the L&F. + * + * @return the default actions + */ + private ActionMap createDefaultActions() + { + ActionMapUIResource am = new ActionMapUIResource(); + Action action = new FocusAction(); + am.put(action.getValue(Action.NAME), action); + return am; } /** @@ -226,9 +311,10 @@ public class BasicMenuBarUI extends MenuBarUI * This method reverses the work done in installKeyboardActions. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: implement. + SwingUtilities.replaceUIInputMap(menuBar, + JComponent.WHEN_IN_FOCUSED_WINDOW, null); + SwingUtilities.replaceUIActionMap(menuBar, null); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java index 63a09bff6a2..bbc08535cdc 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.SystemProperties; + import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -52,11 +54,15 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import javax.swing.AbstractAction; +import javax.swing.AbstractButton; import javax.swing.ActionMap; import javax.swing.ButtonModel; import javax.swing.Icon; @@ -237,7 +243,8 @@ public class BasicMenuItemUI extends MenuItemUI */ public void propertyChange(PropertyChangeEvent e) { - if (e.getPropertyName() == "accelerator") + String property = e.getPropertyName(); + if (property.equals("accelerator")) { InputMap map = SwingUtilities.getUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW); @@ -250,6 +257,22 @@ public class BasicMenuItemUI extends MenuItemUI if (accelerator != null) map.put(accelerator, "doClick"); } + // TextLayout caching for speed-up drawing of text. + else if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY) + || property.equals("font")) + && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") + == null) + + { + AbstractButton b = (AbstractButton) e.getSource(); + String text = b.getText(); + if (text == null) + text = ""; + FontRenderContext frc = new FontRenderContext(new AffineTransform(), + false, false); + TextLayout layout = new TextLayout(text, b.getFont(), frc); + b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout); + } } } @@ -833,12 +856,13 @@ public class BasicMenuItemUI extends MenuItemUI int mnemonicIndex = menuItem.getDisplayedMnemonicIndex(); if (mnemonicIndex != -1) - BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemonicIndex, + BasicGraphicsUtils.drawStringUnderlineCharAt(menuItem, g, text, + mnemonicIndex, textRect.x, textRect.y + fm.getAscent()); else - BasicGraphicsUtils.drawString(g, text, 0, textRect.x, + BasicGraphicsUtils.drawString(menuItem, g, text, 0, textRect.x, textRect.y + fm.getAscent()); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java index f8936be5b66..7d8784fd15a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java @@ -220,9 +220,8 @@ public class BasicMenuUI extends BasicMenuItemUI * */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: Need to implement + super.installKeyboardActions(); } /** @@ -230,13 +229,12 @@ public class BasicMenuUI extends BasicMenuItemUI */ protected void installListeners() { - ((JMenu) menuItem).addMouseListener(mouseInputListener); - ((JMenu) menuItem).addMouseMotionListener(mouseInputListener); + super.installListeners(); ((JMenu) menuItem).addMenuListener(menuListener); - ((JMenu) menuItem).addMenuDragMouseListener(menuDragMouseListener); } protected void setupPostTimer(JMenu menu) + throws NotImplementedException { // TODO: Implement this properly. } @@ -265,9 +263,8 @@ public class BasicMenuUI extends BasicMenuItemUI * Basic look and feel's defaults. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: Need to implement + super.installKeyboardActions(); } /** @@ -276,9 +273,8 @@ public class BasicMenuUI extends BasicMenuItemUI */ protected void uninstallListeners() { - ((JMenu) menuItem).removeMouseListener(mouseInputListener); + super.uninstallListeners(); ((JMenu) menuItem).removeMenuListener(menuListener); - ((JMenu) menuItem).removePropertyChangeListener(propertyChangeListener); } /** @@ -351,7 +347,7 @@ public class BasicMenuUI extends BasicMenuItemUI public void mouseMoved(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } public void mousePressed(MouseEvent e) @@ -472,7 +468,8 @@ public class BasicMenuUI extends BasicMenuItemUI */ public ChangeHandler(JMenu m, BasicMenuUI ui) { - // Not used. + menu = m; + this.ui = ui; } /** @@ -520,7 +517,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuDragMouseExited(MenuDragMouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -531,7 +528,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuDragMouseReleased(MenuDragMouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } } @@ -548,7 +545,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuKeyPressed(MenuKeyEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -558,7 +555,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuKeyReleased(MenuKeyEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -568,6 +565,7 @@ public class BasicMenuUI extends BasicMenuItemUI * @param e A {@link MenuKeyEvent}. */ public void menuKeyTyped(MenuKeyEvent e) + throws NotImplementedException { // TODO: What should be done here, if anything? } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java index 9acf8210d9e..e2380858098 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -38,13 +38,12 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Font; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -58,10 +57,14 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.Icon; +import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; @@ -76,6 +79,7 @@ import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.Border; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.OptionPaneUI; @@ -85,6 +89,21 @@ import javax.swing.plaf.OptionPaneUI; public class BasicOptionPaneUI extends OptionPaneUI { /** + * Implements the "close" keyboard action. + */ + static class OptionPaneCloseAction + extends AbstractAction + { + + public void actionPerformed(ActionEvent event) + { + JOptionPane op = (JOptionPane) event.getSource(); + op.setValue(new Integer(JOptionPane.CLOSED_OPTION)); + } + + } + + /** * This is a helper class that listens to the buttons located at the bottom * of the JOptionPane. * @@ -389,36 +408,20 @@ public class BasicOptionPaneUI extends OptionPaneUI */ public void propertyChange(PropertyChangeEvent e) { - if (e.getPropertyName().equals(JOptionPane.ICON_PROPERTY) - || e.getPropertyName().equals(JOptionPane.MESSAGE_TYPE_PROPERTY)) - addIcon(messageAreaContainer); - else if (e.getPropertyName().equals(JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY)) - resetSelectedValue(); - else if (e.getPropertyName().equals(JOptionPane.INITIAL_VALUE_PROPERTY) - || e.getPropertyName().equals(JOptionPane.OPTIONS_PROPERTY) - || e.getPropertyName().equals(JOptionPane.OPTION_TYPE_PROPERTY)) + String property = e.getPropertyName(); + if (property.equals(JOptionPane.ICON_PROPERTY) + || property.equals(JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY) + || property.equals(JOptionPane.INITIAL_VALUE_PROPERTY) + || property.equals(JOptionPane.MESSAGE_PROPERTY) + || property.equals(JOptionPane.MESSAGE_TYPE_PROPERTY) + || property.equals(JOptionPane.OPTION_TYPE_PROPERTY) + || property.equals(JOptionPane.OPTIONS_PROPERTY) + || property.equals(JOptionPane.WANTS_INPUT_PROPERTY)) { - Container newButtons = createButtonArea(); - optionPane.remove(buttonContainer); - optionPane.add(newButtons); - buttonContainer = newButtons; + uninstallComponents(); + installComponents(); + optionPane.validate(); } - - else if (e.getPropertyName().equals(JOptionPane.MESSAGE_PROPERTY) - || e.getPropertyName().equals(JOptionPane.WANTS_INPUT_PROPERTY) - || e.getPropertyName().equals(JOptionPane.SELECTION_VALUES_PROPERTY)) - { - optionPane.remove(messageAreaContainer); - messageAreaContainer = createMessageArea(); - optionPane.add(messageAreaContainer); - Container newButtons = createButtonArea(); - optionPane.remove(buttonContainer); - optionPane.add(newButtons); - buttonContainer = newButtons; - optionPane.add(buttonContainer); - } - optionPane.invalidate(); - optionPane.repaint(); } } @@ -460,17 +463,7 @@ public class BasicOptionPaneUI extends OptionPaneUI protected JOptionPane optionPane; /** The size of the icons. */ - // FIXME: wrong name for a constant. - private static final int iconSize = 36; - - /** The foreground color for the message area. */ - private transient Color messageForeground; - - /** The border around the message area. */ - private transient Border messageBorder; - - /** The border around the button area. */ - private transient Border buttonBorder; + private static final int ICON_SIZE = 36; /** The string used to describe OK buttons. */ private static final String OK_STRING = "OK"; @@ -505,7 +498,7 @@ public class BasicOptionPaneUI extends OptionPaneUI */ public int getIconWidth() { - return iconSize; + return ICON_SIZE; } /** @@ -515,7 +508,7 @@ public class BasicOptionPaneUI extends OptionPaneUI */ public int getIconHeight() { - return iconSize; + return ICON_SIZE; } /** @@ -566,7 +559,7 @@ public class BasicOptionPaneUI extends OptionPaneUI // Should be purple. g.setColor(Color.RED); - g.fillOval(0, 0, iconSize, iconSize); + g.fillOval(0, 0, ICON_SIZE, ICON_SIZE); g.setColor(Color.BLACK); g.drawOval(16, 6, 4, 4); @@ -615,7 +608,7 @@ public class BasicOptionPaneUI extends OptionPaneUI Color saved = g.getColor(); g.setColor(Color.GREEN); - g.fillRect(0, 0, iconSize, iconSize); + g.fillRect(0, 0, ICON_SIZE, ICON_SIZE); g.setColor(Color.BLACK); @@ -623,7 +616,7 @@ public class BasicOptionPaneUI extends OptionPaneUI g.drawOval(14, 5, 10, 10); g.setColor(Color.GREEN); - g.fillRect(0, 10, iconSize, iconSize - 10); + g.fillRect(0, 10, ICON_SIZE, ICON_SIZE - 10); g.setColor(Color.BLACK); @@ -640,10 +633,6 @@ public class BasicOptionPaneUI extends OptionPaneUI } }; - // FIXME: Uncomment when the ImageIcons are fixed. - - /* IconUIResource warningIcon, questionIcon, infoIcon, errorIcon;*/ - /** * Creates a new BasicOptionPaneUI object. */ @@ -705,6 +694,7 @@ public class BasicOptionPaneUI extends OptionPaneUI if (icon != null) { iconLabel = new JLabel(icon); + configureLabel(iconLabel); top.add(iconLabel, BorderLayout.WEST); } } @@ -766,7 +756,9 @@ public class BasicOptionPaneUI extends OptionPaneUI } else if (msg instanceof Icon) { - container.add(new JLabel((Icon) msg), cons); + JLabel label = new JLabel((Icon) msg); + configureLabel(label); + container.add(label, cons); cons.gridy++; } else @@ -783,8 +775,11 @@ public class BasicOptionPaneUI extends OptionPaneUI addMessageComponents(container, cons, tmp, maxll, true); } else - addMessageComponents(container, cons, new JLabel(msg.toString()), - maxll, true); + { + JLabel label = new JLabel(msg.toString()); + configureLabel(label); + addMessageComponents(container, cons, label, maxll, true); + } } } @@ -815,6 +810,7 @@ public class BasicOptionPaneUI extends OptionPaneUI remainder = d.substring(maxll); } JLabel label = new JLabel(line); + configureLabel(label); c.add(label); // If there is nothing left to burst, then we can stop. @@ -825,8 +821,12 @@ public class BasicOptionPaneUI extends OptionPaneUI if (remainder.length() > maxll || remainder.contains("\n")) burstStringInto(c, remainder, maxll); else - // Add the remainder to the container and be done. - c.add(new JLabel(remainder)); + { + // Add the remainder to the container and be done. + JLabel l = new JLabel(remainder); + configureLabel(l); + c.add(l); + } } /** @@ -862,6 +862,9 @@ public class BasicOptionPaneUI extends OptionPaneUI protected Container createButtonArea() { JPanel buttonPanel = new JPanel(); + Border b = UIManager.getBorder("OptionPane.buttonAreaBorder"); + if (b != null) + buttonPanel.setBorder(b); buttonPanel.setLayout(createLayoutManager()); addButtonComponents(buttonPanel, getButtons(), getInitialValueIndex()); @@ -887,6 +890,10 @@ public class BasicOptionPaneUI extends OptionPaneUI protected Container createMessageArea() { JPanel messageArea = new JPanel(); + Border messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder"); + if (messageBorder != null) + messageArea.setBorder(messageBorder); + messageArea.setLayout(new BorderLayout()); addIcon(messageArea); @@ -941,8 +948,9 @@ public class BasicOptionPaneUI extends OptionPaneUI */ protected Container createSeparator() { - // FIXME: Figure out what this method is supposed to return and where - // this should be added to the OptionPane. + // The reference implementation returns null here. When overriding + // to return something non-null, the component gets added between + // the message area and the button area. See installComponents(). return null; } @@ -1143,35 +1151,17 @@ public class BasicOptionPaneUI extends OptionPaneUI */ protected void installComponents() { - // reset it. - hasCustomComponents = false; - Container msg = createMessageArea(); - if (msg != null) - { - ((JComponent) msg).setBorder(messageBorder); - msg.setForeground(messageForeground); - messageAreaContainer = msg; - optionPane.add(msg); - } + // First thing is the message area. + optionPane.add(createMessageArea()); - // FIXME: Figure out if the separator should be inserted here or what - // this thing is supposed to do. Note: The JDK does NOT insert another - // component at this place. The JOptionPane only has two panels in it - // and there actually are applications that depend on this beeing so. + // Add separator when createSeparator() is overridden to return + // something other than null. Container sep = createSeparator(); if (sep != null) optionPane.add(sep); - Container button = createButtonArea(); - if (button != null) - { - ((JComponent) button).setBorder(buttonBorder); - buttonContainer = button; - optionPane.add(button); - } - - optionPane.setBorder(BorderFactory.createEmptyBorder(12, 12, 11, 11)); - optionPane.invalidate(); + // Last thing is the button area. + optionPane.add(createButtonArea()); } /** @@ -1185,10 +1175,6 @@ public class BasicOptionPaneUI extends OptionPaneUI LookAndFeel.installBorder(optionPane, "OptionPane.border"); optionPane.setOpaque(true); - messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder"); - messageForeground = UIManager.getColor("OptionPane.messageForeground"); - buttonBorder = UIManager.getBorder("OptionPane.buttonAreaBorder"); - minimumSize = UIManager.getDimension("OptionPane.minimumSize"); // FIXME: Image icons don't seem to work properly right now. @@ -1206,9 +1192,44 @@ public class BasicOptionPaneUI extends OptionPaneUI * This method installs keyboard actions for the JOptionpane. */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: implement. + // Install the input map. + Object[] bindings = + (Object[]) SharedUIDefaults.get("OptionPane.windowBindings"); + InputMap inputMap = LookAndFeel.makeComponentInputMap(optionPane, + bindings); + SwingUtilities.replaceUIInputMap(optionPane, + JComponent.WHEN_IN_FOCUSED_WINDOW, + inputMap); + + // FIXME: The JDK uses a LazyActionMap for parentActionMap + SwingUtilities.replaceUIActionMap(optionPane, getActionMap()); + } + + /** + * Fetches the action map from the UI defaults, or create a new one + * if the action map hasn't been initialized. + * + * @return the action map + */ + private ActionMap getActionMap() + { + ActionMap am = (ActionMap) UIManager.get("OptionPane.actionMap"); + if (am == null) + { + am = createDefaultActions(); + UIManager.getLookAndFeelDefaults().put("OptionPane.actionMap", am); + } + return am; + } + + private ActionMap createDefaultActions() + { + ActionMapUIResource am = new ActionMapUIResource(); + Action action = new OptionPaneCloseAction(); + + am.put("close", action); + return am; } /** @@ -1321,10 +1342,6 @@ public class BasicOptionPaneUI extends OptionPaneUI minimumSize = null; - messageBorder = null; - buttonBorder = null; - messageForeground = null; - // FIXME: ImageIcons don't seem to work properly /* @@ -1339,9 +1356,10 @@ public class BasicOptionPaneUI extends OptionPaneUI * This method uninstalls keyboard actions for the JOptionPane. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: implement. + SwingUtilities.replaceUIInputMap(optionPane, JComponent. + WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); + SwingUtilities.replaceUIActionMap(optionPane, null); } /** @@ -1367,4 +1385,20 @@ public class BasicOptionPaneUI extends OptionPaneUI optionPane = null; } + + /** + * Applies the proper UI configuration to labels that are added to + * the OptionPane. + * + * @param l the label to configure + */ + private void configureLabel(JLabel l) + { + Color c = UIManager.getColor("OptionPane.messageForeground"); + if (c != null) + l.setForeground(c); + Font f = UIManager.getFont("OptionPane.messageFont"); + if (f != null) + l.setFont(f); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java index a26a5c7c46b..8c0fe6757e3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -37,33 +37,574 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.Dimension; +import java.awt.KeyboardFocusManager; +import java.awt.event.ActionEvent; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.MouseEvent; +import java.util.EventListener; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.BoxLayout; +import javax.swing.InputMap; +import javax.swing.JApplet; import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import javax.swing.JRootPane; import javax.swing.LookAndFeel; import javax.swing.MenuElement; import javax.swing.MenuSelectionManager; import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.PopupMenuUI; - /** * UI Delegate for JPopupMenu */ public class BasicPopupMenuUI extends PopupMenuUI { + /** + * Handles keyboard navigation through menus. + */ + private static class NavigateAction + extends AbstractAction + { + + /** + * Creates a new NavigateAction instance. + * + * @param name the name of the action + */ + NavigateAction(String name) + { + super(name); + } + + /** + * Actually performs the action. + */ + public void actionPerformed(ActionEvent event) + { + String name = (String) getValue(Action.NAME); + if (name.equals("selectNext")) + navigateNextPrevious(true); + else if (name.equals("selectPrevious")) + navigateNextPrevious(false); + else if (name.equals("selectChild")) + navigateParentChild(true); + else if (name.equals("selectParent")) + navigateParentChild(false); + else if (name.equals("cancel")) + cancel(); + else if (name.equals("return")) + doReturn(); + else + assert false : "Must not reach here"; + } + + /** + * Navigates to the next or previous menu item. + * + * @param dir <code>true</code>: navigate to next, <code>false</code>: + * navigate to previous + */ + private void navigateNextPrevious(boolean dir) + { + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + MenuElement path[] = msm.getSelectedPath(); + int len = path.length; + if (len >= 2) + { + + if (path[0] instanceof JMenuBar && + path[1] instanceof JMenu && len == 2) + { + + // A toplevel menu is selected, but its popup not yet shown. + // Show the popup and select the first item + JPopupMenu popup = ((JMenu)path[1]).getPopupMenu(); + MenuElement next = + findEnabledChild(popup.getSubElements(), -1, true); + MenuElement[] newPath; + + if (next != null) + { + newPath = new MenuElement[4]; + newPath[3] = next; + } + else + { + // Menu has no enabled items, show the popup anyway. + newPath = new MenuElement[3]; + } + System.arraycopy(path, 0, newPath, 0, 2); + newPath[2] = popup; + msm.setSelectedPath(newPath); + } + else if (path[len - 1] instanceof JPopupMenu && + path[len - 2] instanceof JMenu) + { + // Select next item in already shown popup menu. + JMenu menu = (JMenu) path[len - 2]; + JPopupMenu popup = menu.getPopupMenu(); + MenuElement next = + findEnabledChild(popup.getSubElements(), -1, dir); + + if (next != null) + { + MenuElement[] newPath = new MenuElement[len + 1]; + System.arraycopy(path, 0, newPath, 0, len); + newPath[len] = next; + msm.setSelectedPath(newPath); + } + else + { + // All items in the popup are disabled. + // Find the parent popup menu and select + // its next item. If there's no parent popup menu , do nothing. + if (len > 2 && path[len - 3] instanceof JPopupMenu) + { + popup = ((JPopupMenu) path[len - 3]); + next = findEnabledChild(popup.getSubElements(), + menu, dir); + if (next != null && next != menu) + { + MenuElement[] newPath = new MenuElement[len - 1]; + System.arraycopy(path, 0, newPath, 0, len - 2); + newPath[len - 2] = next; + msm.setSelectedPath(newPath); + } + } + } + } + else + { + // Only select the next item. + MenuElement subs[] = path[len - 2].getSubElements(); + MenuElement nextChild = + findEnabledChild(subs, path[len - 1], dir); + if (nextChild == null) + { + nextChild = findEnabledChild(subs, -1, dir); + } + if (nextChild != null) + { + path[len-1] = nextChild; + msm.setSelectedPath(path); + } + } + } + } + + private MenuElement findEnabledChild(MenuElement[] children, + MenuElement start, boolean dir) + { + MenuElement found = null; + for (int i = 0; i < children.length && found == null; i++) + { + if (children[i] == start) + { + found = findEnabledChild(children, i, dir); + } + } + return found; + } + + /** + * Searches the next or previous enabled child menu element. + * + * @param children the children to search through + * @param start the index at which to start + * @param dir the direction (true == forward, false == backward) + * + * @return the found element or null + */ + private MenuElement findEnabledChild(MenuElement[] children, + int start, boolean dir) + { + MenuElement result = null; + if (dir) + { + result = findNextEnabledChild(children, start + 1, children.length-1); + if (result == null) + result = findNextEnabledChild(children, 0, start - 1); + } + else + { + result = findPreviousEnabledChild(children, start - 1, 0); + if (result == null) + result = findPreviousEnabledChild(children, children.length-1, + start + 1); + } + return result; + } + + /** + * Finds the next child element that is enabled and visible. + * + * @param children the children to search through + * @param start the start index + * @param end the end index + * + * @return the found child, or null + */ + private MenuElement findNextEnabledChild(MenuElement[] children, int start, + int end) + { + MenuElement found = null; + for (int i = start; i <= end && found == null; i++) + { + if (children[i] != null) + { + Component comp = children[i].getComponent(); + if (comp != null && comp.isEnabled() && comp.isVisible()) + { + found = children[i]; + } + } + } + return found; + } + + /** + * Finds the previous child element that is enabled and visible. + * + * @param children the children to search through + * @param start the start index + * @param end the end index + * + * @return the found child, or null + */ + private MenuElement findPreviousEnabledChild(MenuElement[] children, + int start, int end) + { + MenuElement found = null; + for (int i = start; i >= end && found == null; i--) + { + if (children[i] != null) + { + Component comp = children[i].getComponent(); + if (comp != null && comp.isEnabled() && comp.isVisible()) + { + found = children[i]; + } + } + } + return found; + } + + /** + * Navigates to the parent or child menu item. + * + * @param selectChild <code>true</code>: navigate to child, + * <code>false</code>: navigate to parent + */ + private void navigateParentChild(boolean selectChild) + { + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + MenuElement path[] = msm.getSelectedPath(); + int len = path.length; + + if (selectChild) + { + if (len > 0 && path[len - 1] instanceof JMenu + && ! ((JMenu) path[len-1]).isTopLevelMenu()) + { + // We have a submenu, open it. + JMenu menu = (JMenu) path[len - 1]; + JPopupMenu popup = menu.getPopupMenu(); + MenuElement[] subs = popup.getSubElements(); + MenuElement item = findEnabledChild(subs, -1, true); + MenuElement[] newPath; + + if (item == null) + { + newPath = new MenuElement[len + 1]; + } + else + { + newPath = new MenuElement[len + 2]; + newPath[len + 1] = item; + } + System.arraycopy(path, 0, newPath, 0, len); + newPath[len] = popup; + msm.setSelectedPath(newPath); + return; + } + } + else + { + int popupIndex = len-1; + if (len > 2 + && (path[popupIndex] instanceof JPopupMenu + || path[--popupIndex] instanceof JPopupMenu) + && ! ((JMenu) path[popupIndex - 1]).isTopLevelMenu()) + { + // We have a submenu, close it. + MenuElement newPath[] = new MenuElement[popupIndex]; + System.arraycopy(path, 0, newPath, 0, popupIndex); + msm.setSelectedPath(newPath); + return; + } + } + + // If we got here, we have not selected a child or parent. + // Check if we have a toplevel menu selected. If so, then select + // another one. + if (len > 1 && path[0] instanceof JMenuBar) + { + MenuElement currentMenu = path[1]; + MenuElement nextMenu = findEnabledChild(path[0].getSubElements(), + currentMenu, selectChild); + + if (nextMenu != null && nextMenu != currentMenu) + { + MenuElement newSelection[]; + if (len == 2) + { + // Menu is selected but its popup not shown. + newSelection = new MenuElement[2]; + newSelection[0] = path[0]; + newSelection[1] = nextMenu; + } + else + { + // Menu is selected and its popup is shown. + newSelection = new MenuElement[3]; + newSelection[0] = path[0]; + newSelection[1] = nextMenu; + newSelection[2] = ((JMenu) nextMenu).getPopupMenu(); + } + msm.setSelectedPath(newSelection); + } + } + } + + /** + * Handles cancel requests (ESC key). + */ + private void cancel() + { + // Fire popup menu cancelled event. Unfortunately the + // firePopupMenuCancelled() is protected in JPopupMenu so we work + // around this limitation by fetching the listeners and notifying them + // directly. + JPopupMenu lastPopup = (JPopupMenu) getLastPopup(); + EventListener[] ll = lastPopup.getListeners(PopupMenuListener.class); + for (int i = 0; i < ll.length; i++) + { + PopupMenuEvent ev = new PopupMenuEvent(lastPopup); + ((PopupMenuListener) ll[i]).popupMenuCanceled(ev); + } + + // Close the last popup or the whole selection if there's only one + // popup left. + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + MenuElement path[] = msm.getSelectedPath(); + if(path.length > 4) + { + MenuElement newPath[] = new MenuElement[path.length - 2]; + System.arraycopy(path,0,newPath,0,path.length-2); + MenuSelectionManager.defaultManager().setSelectedPath(newPath); + } + else + msm.clearSelectedPath(); + } + + /** + * Returns the last popup menu in the current selection or null. + * + * @return the last popup menu in the current selection or null + */ + private JPopupMenu getLastPopup() + { + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + MenuElement[] p = msm.getSelectedPath(); + JPopupMenu popup = null; + for(int i = p.length - 1; popup == null && i >= 0; i--) + { + if (p[i] instanceof JPopupMenu) + popup = (JPopupMenu) p[i]; + } + return popup; + } + + /** + * Handles ENTER key requests. This normally opens submenus on JMenu + * items, or activates the menu item as if it's been clicked on it. + */ + private void doReturn() + { + KeyboardFocusManager fmgr = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + Component focusOwner = fmgr.getFocusOwner(); + if((focusOwner == null || (focusOwner instanceof JRootPane))) + { + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + MenuElement path[] = msm.getSelectedPath(); + MenuElement lastElement; + if(path.length > 0) + { + lastElement = path[path.length - 1]; + if(lastElement instanceof JMenu) + { + MenuElement newPath[] = new MenuElement[path.length + 1]; + System.arraycopy(path,0,newPath,0,path.length); + newPath[path.length] = ((JMenu) lastElement).getPopupMenu(); + msm.setSelectedPath(newPath); + } + else if(lastElement instanceof JMenuItem) + { + JMenuItem mi = (JMenuItem)lastElement; + if (mi.getUI() instanceof BasicMenuItemUI) + { + ((BasicMenuItemUI)mi.getUI()).doClick(msm); + } + else + { + msm.clearSelectedPath(); + mi.doClick(0); + } + } + } + } + } + } + + /** + * Installs keyboard actions when a popup is opened, and uninstalls the + * keyboard actions when closed. This listens on the default + * MenuSelectionManager. + */ + private class KeyboardHelper + implements ChangeListener + { + private MenuElement[] lastSelectedPath = new MenuElement[0]; + private Component lastFocused; + private JRootPane invokerRootPane; + + public void stateChanged(ChangeEvent event) + { + MenuSelectionManager msm = (MenuSelectionManager) event.getSource(); + MenuElement[] p = msm.getSelectedPath(); + JPopupMenu popup = getActivePopup(p); + if (popup == null || popup.isFocusable()) + { + if (lastSelectedPath.length != 0 && p.length != 0 ) + { + if (! invokerEquals(p[0], lastSelectedPath[0])) + { + uninstallKeyboardActionsImpl(); + lastSelectedPath = new MenuElement[0]; + } + } + + if (lastSelectedPath.length == 0 && p.length > 0) + { + JComponent invoker; + if (popup == null) + { + if (p.length == 2 && p[0] instanceof JMenuBar + && p[1] instanceof JMenu) + { + // A menu has been selected but not opened. + invoker = (JComponent)p[1]; + popup = ((JMenu)invoker).getPopupMenu(); + } + else + { + return; + } + } + else + { + Component c = popup.getInvoker(); + if(c instanceof JFrame) + { + invoker = ((JFrame) c).getRootPane(); + } + else if(c instanceof JApplet) + { + invoker = ((JApplet) c).getRootPane(); + } + else + { + while (!(c instanceof JComponent)) + { + if (c == null) + { + return; + } + c = c.getParent(); + } + invoker = (JComponent)c; + } + } + + // Remember current focus owner. + lastFocused = KeyboardFocusManager. + getCurrentKeyboardFocusManager().getFocusOwner(); + + // Install keybindings used for menu navigation. + invokerRootPane = SwingUtilities.getRootPane(invoker); + if (invokerRootPane != null) + { + invokerRootPane.requestFocus(true); + installKeyboardActionsImpl(); + } + } + else if (lastSelectedPath.length != 0 && p.length == 0) + { + // menu hidden -- return focus to where it had been before + // and uninstall menu keybindings + uninstallKeyboardActionsImpl(); + } + } + + // Remember the last path selected + lastSelectedPath = p; + } + + private JPopupMenu getActivePopup(MenuElement[] path) + { + JPopupMenu active = null; + for (int i = path.length - 1; i >= 0 && active == null; i--) + { + MenuElement elem = path[i]; + if (elem instanceof JPopupMenu) + { + active = (JPopupMenu) elem; + } + } + return active; + } + + private boolean invokerEquals(MenuElement el1, MenuElement el2) + { + Component invoker1 = el1.getComponent(); + Component invoker2 = el2.getComponent(); + if (invoker1 instanceof JPopupMenu) + invoker1 = ((JPopupMenu) invoker1).getInvoker(); + if (invoker2 instanceof JPopupMenu) + invoker2 = ((JPopupMenu) invoker2).getInvoker(); + return invoker1 == invoker2; + } + } + /* popupMenu for which this UI delegate is for*/ protected JPopupMenu popupMenu; @@ -75,6 +616,19 @@ public class BasicPopupMenuUI extends PopupMenuUI TopWindowListener topWindowListener; /** + * Counts how many popup menus are handled by this UI or a subclass. + * This is used to install a KeyboardHelper on the MenuSelectionManager + * for the first popup, and uninstall this same KeyboardHelper when the + * last popup is uninstalled. + */ + private static int numPopups; + + /** + * This is the KeyboardHelper that listens on the MenuSelectionManager. + */ + private static KeyboardHelper keyboardHelper; + + /** * Creates a new BasicPopupMenuUI object. */ public BasicPopupMenuUI() @@ -106,6 +660,16 @@ public class BasicPopupMenuUI extends PopupMenuUI public void installUI(JComponent c) { super.installUI(c); + + // Install KeyboardHelper when the first popup is initialized. + if (numPopups == 0) + { + keyboardHelper = new KeyboardHelper(); + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + msm.addChangeListener(keyboardHelper); + } + numPopups++; + popupMenu = (JPopupMenu) c; popupMenu.setLayout(new DefaultMenuLayout(popupMenu, BoxLayout.Y_AXIS)); popupMenu.setBorderPainted(true); @@ -113,6 +677,7 @@ public class BasicPopupMenuUI extends PopupMenuUI installDefaults(); installListeners(); + installKeyboardActions(); } /** @@ -139,9 +704,77 @@ public class BasicPopupMenuUI extends PopupMenuUI * This method installs the keyboard actions for this {@link JPopupMenu}. */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: Need to implement + // We can't install the keyboard actions here, because then all + // popup menus would have their actions registered in the KeyboardManager. + // So we install it when the popup menu is opened, and uninstall it + // when it's closed. This is done in the KeyboardHelper class. + // Install InputMap. + } + + /** + * Called by the KeyboardHandler when a popup is made visible. + */ + void installKeyboardActionsImpl() + { + Object[] bindings; + if (popupMenu.getComponentOrientation().isLeftToRight()) + { + bindings = (Object[]) + SharedUIDefaults.get("PopupMenu.selectedWindowInputMapBindings"); + } + else + { + bindings = (Object[]) SharedUIDefaults.get + ("PopupMenu.selectedWindowInputMapBindings.RightToLeft"); + } + InputMap inputMap = LookAndFeel.makeComponentInputMap(popupMenu, bindings); + SwingUtilities.replaceUIInputMap(popupMenu, + JComponent.WHEN_IN_FOCUSED_WINDOW, + inputMap); + + // Install ActionMap. + SwingUtilities.replaceUIActionMap(popupMenu, getActionMap()); + } + + /** + * Creates and returns the shared action map for JTrees. + * + * @return the shared action map for JTrees + */ + private ActionMap getActionMap() + { + ActionMap am = (ActionMap) UIManager.get("PopupMenu.actionMap"); + if (am == null) + { + am = createDefaultActions(); + UIManager.getLookAndFeelDefaults().put("PopupMenu.actionMap", am); + } + return am; + } + + /** + * Creates the default actions when there are none specified by the L&F. + * + * @return the default actions + */ + private ActionMap createDefaultActions() + { + ActionMapUIResource am = new ActionMapUIResource(); + Action action = new NavigateAction("selectNext"); + am.put(action.getValue(Action.NAME), action); + action = new NavigateAction("selectPrevious"); + am.put(action.getValue(Action.NAME), action); + action = new NavigateAction("selectParent"); + am.put(action.getValue(Action.NAME), action); + action = new NavigateAction("selectChild"); + am.put(action.getValue(Action.NAME), action); + action = new NavigateAction("return"); + am.put(action.getValue(Action.NAME), action); + action = new NavigateAction("cancel"); + am.put(action.getValue(Action.NAME), action); + + return am; } /** @@ -155,7 +788,17 @@ public class BasicPopupMenuUI extends PopupMenuUI { uninstallListeners(); uninstallDefaults(); + uninstallKeyboardActions(); popupMenu = null; + + // Install KeyboardHelper when the first popup is initialized. + numPopups--; + if (numPopups == 0) + { + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + msm.removeChangeListener(keyboardHelper); + } + } /** @@ -182,9 +825,22 @@ public class BasicPopupMenuUI extends PopupMenuUI * Uninstalls any keyboard actions. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: Need to implement + // We can't install the keyboard actions here, because then all + // popup menus would have their actions registered in the KeyboardManager. + // So we install it when the popup menu is opened, and uninstall it + // when it's closed. This is done in the KeyboardHelper class. + // Install InputMap. + } + + /** + * Called by the KeyboardHandler when a popup is made invisible. + */ + void uninstallKeyboardActionsImpl() + { + SwingUtilities.replaceUIInputMap(popupMenu, + JComponent.WHEN_IN_FOCUSED_WINDOW, null); + SwingUtilities.replaceUIActionMap(popupMenu, null); } /** @@ -278,9 +934,10 @@ public class BasicPopupMenuUI extends PopupMenuUI // Adds topWindowListener to top-level window to listener to // ComponentEvents fired by it. We need to cancel this popup menu // if topWindow to which this popup belongs was resized or moved. - Component invoker = popupMenu.getInvoker(); + Component invoker = popupMenu.getInvoker(); Component rootContainer = SwingUtilities.getRoot(invoker); - rootContainer.addComponentListener(topWindowListener); + if (rootContainer != null) + rootContainer.addComponentListener(topWindowListener); // if this popup menu is a free floating popup menu, // then by default its first element should be always selected when diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java index a7da21c4f6e..aed4d69d6d5 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java @@ -42,6 +42,7 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Rectangle; import javax.swing.AbstractButton; @@ -92,14 +93,6 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI protected void installDefaults(AbstractButton b) { super.installDefaults(b); - if (b.getIcon() == null) - b.setIcon(icon); - if (b.getSelectedIcon() == null) - b.setSelectedIcon(icon); - if (b.getDisabledIcon() == null) - b.setDisabledIcon(icon); - if (b.getDisabledSelectedIcon() == null) - b.setDisabledSelectedIcon(icon); } /** @@ -145,16 +138,17 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI g.setFont(f); ButtonModel m = b.getModel(); - Icon currentIcon = null; - if (m.isSelected() && m.isEnabled()) - currentIcon = b.getSelectedIcon(); - else if (! m.isSelected() && m.isEnabled()) - currentIcon = b.getIcon(); - else if (m.isSelected() && ! m.isEnabled()) - currentIcon = b.getDisabledSelectedIcon(); - else // (!m.isSelected() && ! m.isEnabled()) - currentIcon = b.getDisabledIcon(); + // FIXME: Do a filtering on any customized icon if the following property + // is set. + boolean enabled = b.isEnabled(); + + Icon currentIcon = b.getIcon(); + if (currentIcon == null) + { + currentIcon = getDefaultIcon(); + } + SwingUtilities.calculateInnerArea(b, vr); String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), b.getText(), currentIcon, @@ -162,15 +156,57 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI b.getVerticalTextPosition(), b.getHorizontalTextPosition(), vr, ir, tr, b.getIconTextGap() + defaultTextShiftOffset); - if (currentIcon != null) - { - currentIcon.paintIcon(c, g, ir.x, ir.y); - } + currentIcon.paintIcon(c, g, ir.x, ir.y); + if (text != null) paintText(g, b, tr, text); if (b.hasFocus() && b.isFocusPainted() && m.isEnabled()) paintFocus(g, tr, c.getSize()); } + + public Dimension getPreferredSize(JComponent c) + { + // This is basically the same code as in + // BasicGraphicsUtils.getPreferredButtonSize() but takes the default icon + // property into account. JRadioButton and subclasses always have an icon: + // the check box. If the user explicitly changes it with setIcon() that + // one will be used for layout calculations and painting instead. + // The other icon properties are ignored. + AbstractButton b = (AbstractButton) c; + + Rectangle contentRect; + Rectangle viewRect; + Rectangle iconRect = new Rectangle(); + Rectangle textRect = new Rectangle(); + Insets insets = b.getInsets(); + + Icon i = b.getIcon(); + if (i == null) + i = getDefaultIcon(); + + viewRect = new Rectangle(); + + SwingUtilities.layoutCompoundLabel( + b, // for the component orientation + b.getFontMetrics(b.getFont()), + b.getText(), + i, + b.getVerticalAlignment(), + b.getHorizontalAlignment(), + b.getVerticalTextPosition(), + b.getHorizontalTextPosition(), + viewRect, iconRect, textRect, + defaultTextIconGap + defaultTextShiftOffset); + + contentRect = textRect.union(iconRect); + + return new Dimension(insets.left + + contentRect.width + + insets.right + b.getHorizontalAlignment(), + insets.top + + contentRect.height + + insets.bottom); + } /** * Paints the focus indicator for JRadioButtons. diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java index b29026342e0..78e5168fc80 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java @@ -302,8 +302,10 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ public void mouseMoved(MouseEvent e) { - // Not interested in where the mouse - // is unless it is being dragged. + if (thumbRect.contains(e.getPoint())) + thumbRollover = true; + else + thumbRollover = false; } /** @@ -486,6 +488,9 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, /** The scrollbar this UI is acting for. */ protected JScrollBar scrollbar; + + /** True if the mouse is over the thumb. */ + boolean thumbRollover; /** * This method adds a component to the layout. @@ -1401,4 +1406,45 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, value = min; return value; } + + /** + * Returns true if the mouse is over the thumb. + * + * @return true if the mouse is over the thumb. + * + * @since 1.5 + */ + public boolean isThumbRollover() + { + return thumbRollover; + } + + /** + * Set thumbRollover to active. This indicates + * whether or not the mouse is over the thumb. + * + * @param active - true if the mouse is over the thumb. + * + * @since 1.5 + */ + protected void setThumbRollover(boolean active) + { + thumbRollover = active; + } + + /** + * Indicates whether the user can position the thumb with + * a mouse click (i.e. middle button). + * + * @return true if the user can position the thumb with a mouse + * click. + * + * @since 1.5 + */ + public boolean getSupportsAbsolutePositioning() + { + // The positioning feature has not been implemented. + // So, false is always returned. + return false; + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java index 2fb16f12e63..3811eebdfd6 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java @@ -371,6 +371,7 @@ public class BasicSliderUI extends SliderUI */ public void mouseDragged(MouseEvent e) { + dragging = true; if (slider.isEnabled()) { currentMouseX = e.getX(); @@ -450,6 +451,7 @@ public class BasicSliderUI extends SliderUI */ public void mouseReleased(MouseEvent e) { + dragging = false; if (slider.isEnabled()) { currentMouseX = e.getX(); @@ -593,6 +595,9 @@ public class BasicSliderUI extends SliderUI /** True if the slider has focus. */ private transient boolean hasFocus; + + /** True if the user is dragging the slider. */ + boolean dragging; /** * Creates a new Basic look and feel Slider UI. @@ -605,6 +610,18 @@ public class BasicSliderUI extends SliderUI } /** + * Returns true if the user is dragging the slider. + * + * @return true if the slider is being dragged. + * + * @since 1.5 + */ + protected boolean isDragging() + { + return dragging; + } + + /** * Gets the shadow color to be used for this slider. The shadow color is the * color used for drawing the top and left edges of the track. * diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 1b2552837c6..11f25167d21 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -1,5 +1,5 @@ /* BasicTabbedPaneUI.java -- - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -51,6 +49,7 @@ import java.awt.Insets; import java.awt.LayoutManager; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.ActionEvent; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; @@ -60,7 +59,10 @@ import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; import javax.swing.Icon; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JTabbedPane; @@ -72,6 +74,7 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.PanelUI; import javax.swing.plaf.TabbedPaneUI; @@ -80,11 +83,127 @@ import javax.swing.text.View; /** * This is the Basic Look and Feel's UI delegate for JTabbedPane. + * + * @author Lillian Angel (langel@redhat.com) + * @author Kim Ho (kho@redhat.com) + * @author Roman Kennke (kennke@aicas.com) + * @author Robert Schuster (robertschuster@fsfe.org) */ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { + + static class NavigateAction extends AbstractAction + { + int direction; + + NavigateAction(String name, int dir) + { + super(name); + direction = dir; + } + + public void actionPerformed(ActionEvent event) + { + JTabbedPane tp = (JTabbedPane) event.getSource(); + BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI(); + + ui.navigateSelectedTab(direction); + } + + } + + static class NavigatePageDownAction extends AbstractAction + { + + public NavigatePageDownAction() + { + super("navigatePageDown"); + } + + public void actionPerformed(ActionEvent event) + { + JTabbedPane tp = (JTabbedPane) event.getSource(); + BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI(); + + int i = tp.getSelectedIndex(); + + if (i < 0) + i = 0; + + ui.selectNextTabInRun(i); + } + + } + + static class NavigatePageUpAction extends AbstractAction + { + + public NavigatePageUpAction() + { + super("navigatePageUp"); + } + + public void actionPerformed(ActionEvent event) + { + JTabbedPane tp = (JTabbedPane) event.getSource(); + BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI(); + + int i = tp.getSelectedIndex(); + + if (i < 0) + i = 0; + + ui.selectPreviousTabInRun(i); + + } + } + + static class RequestFocusAction extends AbstractAction + { + + public RequestFocusAction() + { + super("requestFocus"); + } + + public void actionPerformed(ActionEvent event) + { + ((JTabbedPane) event.getSource()).requestFocus(); + } + + } + + static class RequestFocusForVisibleComponentAction extends AbstractAction + { + + public RequestFocusForVisibleComponentAction() + { + super("requestFocusForVisibleComponent"); + } + + public void actionPerformed(ActionEvent event) + { + JTabbedPane tp = (JTabbedPane) event.getSource(); + + // FIXME: This should select a suitable component within + // the tab content. However I dont know whether we have + // to search for this component or wether the called is + // supposed to do that. + tp.getSelectedComponent().requestFocus(); + } + + } + /** - * A helper class that handles focus. + * A helper class that handles focus. + * <p>The purpose of this class is to implement a more flexible focus + * handling for the tabbed pane, which is used to determine whether the + * focus indicator should be painted or not. When in scrolling layout + * mode the area containing the tabs is a scrollpane, so simply testing + * whether the tabbed pane has the focus does not work.</p> + * <p>The <code>FocusHandler</code> is installed on the scrollpane and + * the tabbed pane and sets the variable <code>hasFocus</code> to + * <code>false</code> only when both components do not hold the focus.</p> * * @specnote Apparently this class was intended to be protected, * but was made public by a compiler bug and is now @@ -99,7 +218,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void focusGained(FocusEvent e) { - // FIXME: Implement. + Object source = e.getSource(); + if (source == panel ) + tabPane.requestFocus(); + else if (source == tabPane) + tabPane.repaint(); } /** @@ -109,7 +232,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void focusLost(FocusEvent e) { - // FIXME: Implement. + if (e.getOppositeComponent() == tabPane.getSelectedComponent()) + tabPane.requestFocus(); + else if (e.getSource() == tabPane) + tabPane.repaint(); } } @@ -124,6 +250,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public class MouseHandler extends MouseAdapter { + public void mouseReleased(MouseEvent e) + { + // Nothing to do here. + } + /** * This method is called when the mouse is pressed. The index cannot * change to a tab that is not enabled. @@ -132,14 +263,84 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void mousePressed(MouseEvent e) { - if (tabPane.isEnabled()) + Object s = e.getSource(); + int placement = tabPane.getTabPlacement(); + + if (s == incrButton) { - int index = tabForCoordinate(tabPane, e.getX(), e.getY()); - if (index >= 0 && tabPane.isEnabledAt(index)) + if(!incrButton.isEnabled()) + return; + + currentScrollLocation++; + + switch (placement) { - tabPane.setSelectedIndex(index); + case JTabbedPane.TOP: + case JTabbedPane.BOTTOM: + currentScrollOffset = getTabAreaInsets(placement).left; + for (int i = 0; i < currentScrollLocation; i++) + currentScrollOffset += rects[i].width; + break; + default: + currentScrollOffset = getTabAreaInsets(placement).top; + for (int i = 0; i < currentScrollLocation; i++) + currentScrollOffset += rects[i].height; + break; } + + updateViewPosition(); + updateButtons(); + + tabPane.repaint(); + } + else if (s == decrButton) + { + if(!decrButton.isEnabled()) + return; + + // The scroll location may be zero but the offset + // greater than zero because of an adjustement to + // make a partially visible tab completely visible. + if (currentScrollLocation > 0) + currentScrollLocation--; + + // Set the offset back to 0 and recompute it. + currentScrollOffset = 0; + + switch (placement) + { + case JTabbedPane.TOP: + case JTabbedPane.BOTTOM: + // Take the tab area inset into account. + if (currentScrollLocation > 0) + currentScrollOffset = getTabAreaInsets(placement).left; + // Recompute scroll offset. + for (int i = 0; i < currentScrollLocation; i++) + currentScrollOffset += rects[i].width; + break; + default: + // Take the tab area inset into account. + if (currentScrollLocation > 0) + currentScrollOffset = getTabAreaInsets(placement).top; + + for (int i = 0; i < currentScrollLocation; i++) + currentScrollOffset += rects[i].height; + } + + updateViewPosition(); + updateButtons(); + + tabPane.repaint(); + } else if (tabPane.isEnabled()) + { + int index = tabForCoordinate(tabPane, e.getX(), e.getY()); + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT + && s == panel) + scrollTab(index, placement); + + tabPane.setSelectedIndex(index); } + } /** @@ -197,6 +398,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { if (e.getPropertyName().equals("tabLayoutPolicy")) { + currentScrollLocation = currentScrollOffset = 0; + layoutManager = createLayoutManager(); tabPane.setLayout(layoutManager); @@ -265,7 +468,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // Find out the minimum/preferred size to display the largest child // of the tabbed pane. - for (int i = 0; i < tabPane.getTabCount(); i++) + int count = tabPane.getTabCount(); + for (int i = 0; i < count; i++) { c = tabPane.getComponentAt(i); if (c == null) @@ -282,21 +486,19 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int min = calculateMaxTabWidth(tabPlacement); - width = Math.max(min, width); - int tabAreaHeight = preferredTabAreaHeight(tabPlacement, - width - tabAreaInsets.left - - tabAreaInsets.right); - height += tabAreaHeight; + width = Math.max(calculateMaxTabWidth(tabPlacement), width); + + height += preferredTabAreaHeight(tabPlacement, + width - tabAreaInsets.left + - tabAreaInsets.right); } else { - int min = calculateMaxTabHeight(tabPlacement); - height = Math.max(min, height); - int tabAreaWidth = preferredTabAreaWidth(tabPlacement, - height - tabAreaInsets.top - - tabAreaInsets.bottom); - width += tabAreaWidth; + height = Math.max(calculateMaxTabHeight(tabPlacement), height); + + width += preferredTabAreaWidth(tabPlacement, + height - tabAreaInsets.top + - tabAreaInsets.bottom); } Insets tabPaneInsets = tabPane.getInsets(); @@ -306,7 +508,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // if tab placement is LEFT OR RIGHT, they share width. // if tab placement is TOP OR BOTTOM, they share height - // PRE STEP: finds the default sizes for the labels as well as their locations. + // PRE STEP: finds the default sizes for the labels as well as their + // locations. // AND where they will be placed within the run system. // 1. calls normalizeTab Runs. // 2. calls rotate tab runs. @@ -345,7 +548,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants break; case RIGHT: maxTabWidth = calculateMaxTabWidth(tabPlacement); - x = size.width - (insets.right + tabAreaInsets.right) - maxTabWidth; + x = size.width - (insets.right + tabAreaInsets.right) + - maxTabWidth - 1; y = insets.top + tabAreaInsets.top; breakAt = size.height - (insets.bottom + tabAreaInsets.bottom); break; @@ -353,7 +557,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants maxTabHeight = calculateMaxTabHeight(tabPlacement); x = insets.left + tabAreaInsets.left; y = size.height - (insets.bottom + tabAreaInsets.bottom) - - maxTabHeight; + - maxTabHeight - 1; breakAt = size.width - (insets.right + tabAreaInsets.right); break; case TOP: @@ -372,6 +576,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants runCount = 0; selectedRun = -1; int selectedIndex = tabPane.getSelectedIndex(); + if (selectedIndex < 0) + selectedIndex = 0; Rectangle rect; @@ -409,7 +615,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants rect.height = maxTabHeight; if (i == selectedIndex) selectedRun = runCount - 1; - } } else @@ -454,9 +659,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int start; if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) - start = y; - else start = x; + else + start = y; normalizeTabRuns(tabPlacement, tabCount, start, breakAt); selectedRun = getRunForTab(tabCount, selectedIndex); if (shouldRotateTabRuns(tabPlacement)) @@ -464,7 +669,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants rotateTabRuns(tabPlacement, selectedRun); } } - + + // Suppress padding if we have only one tab run. + if (runCount == 1) + return; + // Pad the runs. int tabRunOverlay = getTabRunOverlay(tabPlacement); for (int i = runCount - 1; i >= 0; --i) @@ -604,16 +813,18 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public Dimension minimumLayoutSize(Container parent) { - return calculateSize(false); + return calculateSize(true); } - // If there is more free space in an adjacent run AND the tab in the run can fit in the - // adjacent run, move it. This method is not perfect, it is merely an approximation. + // If there is more free space in an adjacent run AND the tab + // in the run can fit in the adjacent run, move it. This method + // is not perfect, it is merely an approximation. // If you play around with Sun's JTabbedPane, you'll see that // it does do some pretty strange things with regards to not moving tabs // that should be moved. // start = the x position where the tabs will begin - // max = the maximum position of where the tabs can go to (tabAreaInsets.left + the width of the tab area) + // max = the maximum position of where the tabs can go to + // (tabAreaInsets.left + the width of the tab area) /** * This method tries to "even out" the number of tabs in each run based on @@ -631,18 +842,20 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingUtilities.TOP || tabPlacement == SwingUtilities.BOTTOM) { - // We should only do this for runCount - 1, cause we can only shift that many times between - // runs. + // We should only do this for runCount - 1, cause we can + // only shift that many times between runs. for (int i = 1; i < runCount; i++) { Rectangle currRun = rects[lastTabInRun(tabCount, i)]; - Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; + Rectangle nextRun = rects[lastTabInRun(tabCount, + getNextTabRun(i))]; int spaceInCurr = currRun.x + currRun.width; int spaceInNext = nextRun.x + nextRun.width; int diffNow = spaceInCurr - spaceInNext; int diffLater = (spaceInCurr - currRun.width) - (spaceInNext + currRun.width); + while (Math.abs(diffLater) < Math.abs(diffNow) && spaceInNext + currRun.width < max) { @@ -654,11 +867,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants diffLater = (spaceInCurr - currRun.width) - (spaceInNext + currRun.width); } - - // Fix the bounds. - int first = lastTabInRun(tabCount, i) + 1; - int last = lastTabInRun(tabCount, getNextTabRun(i)); - int currX = tabAreaInsets.left; + + // Fixes the bounds of all tabs in the current + // run. + int first = tabRuns[i]; + int last = lastTabInRun(tabCount, i); + int currX = start; for (int j = first; j <= last; j++) { rects[j].x = currX; @@ -671,7 +885,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants for (int i = 1; i < runCount; i++) { Rectangle currRun = rects[lastTabInRun(tabCount, i)]; - Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; + Rectangle nextRun = rects[lastTabInRun(tabCount, + getNextTabRun(i))]; int spaceInCurr = currRun.y + currRun.height; int spaceInNext = nextRun.y + nextRun.height; @@ -690,9 +905,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants - (spaceInNext + currRun.height); } - int first = lastTabInRun(tabCount, i) + 1; - int last = lastTabInRun(tabCount, getNextTabRun(i)); - int currY = tabAreaInsets.top; + // Fixes the bounds of tabs in the current run. + int first = tabRuns[i]; + int last = lastTabInRun(tabCount, i); + int currY = start; for (int j = first; j <= last; j++) { rects[j].y = currY; @@ -718,7 +934,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants rects[selectedIndex].height += insets.top + insets.bottom; } - // If the tabs on the run don't fill the width of the window, make it fit now. + // If the tabs on the run don't fill the width of the window, make it + // fit now. // start = starting index of the run // end = last index of the run // max = tabAreaInsets.left + width (or equivalent) @@ -742,7 +959,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int runWidth = rects[end].x + rects[end].width; int spaceRemaining = max - runWidth; int numTabs = end - start + 1; - + // now divvy up the space. int spaceAllocated = spaceRemaining / numTabs; int currX = rects[start].x; @@ -750,11 +967,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { rects[i].x = currX; rects[i].width += spaceAllocated; + currX += rects[i].width; // This is used because since the spaceAllocated // variable is an int, it rounds down. Sometimes, // we don't fill an entire row, so we make it do // so now. + if (i == end && rects[i].x + rects[i].width != max) rects[i].width = max - rects[i].x; } @@ -819,7 +1038,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // The reason why we can't use runCount: // This method is only called to calculate the size request - // for the tabbedPane. However, this size request is dependent on + // for the tabbedPane. However, this size request is dependent on // our desired width. We need to find out what the height would // be IF we got our desired width. for (int i = 0; i < tabPane.getTabCount(); i++) @@ -882,7 +1101,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants runs++; int maxTabWidth = calculateMaxTabWidth(tabPlacement); - int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth); + int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, + maxTabWidth); return tabAreaWidth; } @@ -896,11 +1116,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void rotateTabRuns(int tabPlacement, int selectedRun) { - if (runCount == 1 || selectedRun == 1 || selectedRun == -1) + if (runCount == 1 || selectedRun == 0 || selectedRun == -1) return; int[] newTabRuns = new int[tabRuns.length]; int currentRun = selectedRun; - int i = 1; + int i = 0; do { newTabRuns[i] = tabRuns[currentRun]; @@ -908,8 +1128,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants i++; } while (i < runCount); - if (runCount > 1) - newTabRuns[0] = tabRuns[currentRun]; tabRuns = newTabRuns; BasicTabbedPaneUI.this.selectedRun = 1; @@ -942,7 +1160,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public Dimension preferredLayoutSize(Container parent) { - return super.calculateSize(true); + return super.calculateSize(false); } /** @@ -1016,29 +1234,27 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants SwingUtilities.calculateInnerArea(tabPane, calcRect); Insets tabAreaInsets = getTabAreaInsets(tabPlacement); Insets insets = tabPane.getInsets(); - int runs = 1; - int start = 0; - int top = 0; if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { int maxHeight = calculateMaxTabHeight(tabPlacement); calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; - start = tabAreaInsets.left + insets.left; int width = 0; - int runWidth = start; - top = insets.top + tabAreaInsets.top; + int runWidth = tabAreaInsets.left + insets.left; + int top = insets.top + tabAreaInsets.top; for (int i = 0; i < tabCount; i++) { width = calculateTabWidth(tabPlacement, i, fm); - - rects[i] = new Rectangle(runWidth, top, width, maxHeight); + + // The proper instances should exists because + // assureRectsCreated() was being run already. + rects[i].setBounds(runWidth, top, width, maxHeight); + runWidth += width; } tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; - tabAreaRect.height = runs * maxTabHeight - - (runs - 1) * tabRunOverlay - + tabAreaInsets.top + tabAreaInsets.bottom; + tabAreaRect.height = maxTabHeight + tabAreaInsets.top + + tabAreaInsets.bottom; contentRect.width = tabAreaRect.width; contentRect.height = tabPane.getHeight() - insets.top - insets.bottom - tabAreaRect.height; @@ -1061,23 +1277,25 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; int height = 0; - start = tabAreaInsets.top + insets.top; - int runHeight = start; + int runHeight = tabAreaInsets.top + insets.top;; int fontHeight = fm.getHeight(); - top = insets.left + tabAreaInsets.left; + int left = insets.left + tabAreaInsets.left; for (int i = 0; i < tabCount; i++) { height = calculateTabHeight(tabPlacement, i, fontHeight); - rects[i] = new Rectangle(top, runHeight, maxWidth, height); + + // The proper instances should exists because + // assureRectsCreated() was being run already. + rects[i].setBounds(left, runHeight, maxWidth, height); runHeight += height; } - tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay - + tabAreaInsets.left + tabAreaInsets.right; + tabAreaRect.width = maxTabWidth + tabAreaInsets.left + + tabAreaInsets.right; tabAreaRect.height = tabPane.getHeight() - insets.top - - insets.bottom; + - insets.bottom; tabAreaRect.y = insets.top; contentRect.width = tabPane.getWidth() - insets.left - insets.right - - tabAreaRect.width; + - tabAreaRect.width; contentRect.height = tabAreaRect.height; contentRect.y = insets.top; if (tabPlacement == SwingConstants.LEFT) @@ -1091,11 +1309,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants tabAreaRect.x = contentRect.x + contentRect.width; } } - runCount = runs; - if (runCount > tabRuns.length) - expandTabRunsArray(); - - padSelectedTab(tabPlacement, tabPane.getSelectedIndex()); + + // Unlike the behavior in the WRAP_TAB_LAYOUT the selected + // tab is not padded specially. } /** @@ -1113,8 +1329,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabCount == 0) return; int tabPlacement = tabPane.getTabPlacement(); - incrButton.setVisible(false); - decrButton.setVisible(false); + if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { @@ -1124,18 +1339,49 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Dimension incrDims = incrButton.getPreferredSize(); Dimension decrDims = decrButton.getPreferredSize(); - decrButton.setBounds(tabAreaRect.x + tabAreaRect.width - - incrDims.width - decrDims.width, - tabAreaRect.y, decrDims.width, - tabAreaRect.height); - incrButton.setBounds(tabAreaRect.x + tabAreaRect.width - - incrDims.width, tabAreaRect.y, - decrDims.width, tabAreaRect.height); - + if (tabPlacement == SwingConstants.BOTTOM) + { + // Align scroll buttons with the bottom border of the tabbed + // pane's content area. + decrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width - decrDims.width, + tabAreaRect.y, decrDims.width, + decrDims.height); + incrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width, tabAreaRect.y, + incrDims.width, incrDims.height); + } + else + { + // Align scroll buttons with the top border of the tabbed + // pane's content area. + decrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width - decrDims.width, + tabAreaRect.y + tabAreaRect.height + - decrDims.height, decrDims.width, + decrDims.height); + incrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width, + tabAreaRect.y + tabAreaRect.height + - incrDims.height, + incrDims.width, incrDims.height); + } + tabAreaRect.width -= decrDims.width + incrDims.width; + + updateButtons(); + incrButton.setVisible(true); decrButton.setVisible(true); } + else + { + incrButton.setVisible(false); + decrButton.setVisible(false); + + currentScrollOffset = 0; + currentScrollLocation = 0; + } } if (tabPlacement == SwingConstants.LEFT @@ -1147,34 +1393,54 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Dimension incrDims = incrButton.getPreferredSize(); Dimension decrDims = decrButton.getPreferredSize(); - decrButton.setBounds(tabAreaRect.x, - tabAreaRect.y + tabAreaRect.height - - incrDims.height - decrDims.height, - tabAreaRect.width, decrDims.height); - incrButton.setBounds(tabAreaRect.x, - tabAreaRect.y + tabAreaRect.height - - incrDims.height, tabAreaRect.width, - incrDims.height); + if (tabPlacement == SwingConstants.RIGHT) + { + // Align scroll buttons with the right border of the tabbed + // pane's content area. + decrButton.setBounds(tabAreaRect.x, + tabAreaRect.y + tabAreaRect.height + - incrDims.height - decrDims.height, + decrDims.width, decrDims.height); + incrButton.setBounds(tabAreaRect.x, + tabAreaRect.y + tabAreaRect.height + - incrDims.height, incrDims.width, + incrDims.height); + } + else + { + // Align scroll buttons with the left border of the tabbed + // pane's content area. + decrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - decrDims.width, + tabAreaRect.y + tabAreaRect.height + - incrDims.height - decrDims.height, + decrDims.width, decrDims.height); + incrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width, + tabAreaRect.y + tabAreaRect.height + - incrDims.height, incrDims.width, + incrDims.height); + } tabAreaRect.height -= decrDims.height + incrDims.height; + incrButton.setVisible(true); decrButton.setVisible(true); } + else + { + incrButton.setVisible(false); + decrButton.setVisible(false); + + currentScrollOffset = 0; + currentScrollLocation = 0; + } } viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width, tabAreaRect.height); - int tabC = tabPane.getTabCount() - 1; - if (tabCount > 0) - { - int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width); - int h = Math.max(rects[tabC].height, tabAreaRect.height); - p = findPointForIndex(currentScrollLocation); - - // we want to cover that entire space so that borders that run under - // the tab area don't show up when we move the viewport around. - panel.setSize(w + p.x, h + p.y); - } - viewport.setViewPosition(p); + + updateViewPosition(); + viewport.repaint(); } } @@ -1198,7 +1464,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { selectedRun = getRunForTab(tabPane.getTabCount(), tabPane.getSelectedIndex()); - tabPane.revalidate(); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) + tabPane.revalidate(); tabPane.repaint(); } } @@ -1224,7 +1492,17 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void paint(Graphics g, JComponent c) { - paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); + int placement = tabPane.getTabPlacement(); + g.setColor(highlight); + if (placement == SwingUtilities.TOP + || placement == SwingUtilities.BOTTOM) + g.fillRect(currentScrollOffset, 0, + tabAreaRect.width, tabAreaRect.height); + else + g.fillRect(0, currentScrollOffset, + tabAreaRect.width, tabAreaRect.height); + + paintTabArea(g, placement, tabPane.getSelectedIndex()); } } @@ -1285,6 +1563,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants /** The starting visible tab in the run in SCROLL_TAB_MODE. * This is package-private to avoid an accessor method. */ transient int currentScrollLocation; + + transient int currentScrollOffset; /** A reusable rectangle. */ protected Rectangle calcRect; @@ -1340,16 +1620,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants /** The gap between text and label */ protected int textIconGap; - // Keeps track of tab runs. - // The organization of this array is as follows (lots of experimentation to - // figure this out) - // index 0 = furthest away from the component area (aka outer run) - // index 1 = closest to component area (aka selected run) - // index > 1 = listed in order leading from selected run to outer run. - // each int in the array is the tab index + 1 (counting starts at 1) - // for the last tab in the run. (same as the rects array) - - /** This array keeps track of which tabs are in which run. See above. */ + /** This array keeps track of which tabs are in which run. + * <p>The value at index i denotes the index of the first tab in run i.</p> + * <p>If the value for any index (i > 0) is 0 then (i - 1) is the last + * run.</p> + */ protected int[] tabRuns; /** @@ -1428,7 +1703,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants * The currently visible component. */ private Component visibleComponent; - + + private Color selectedColor; + + private Rectangle tempTextRect = new Rectangle(); + + private Rectangle tempIconRect = new Rectangle(); + /** * Creates a new BasicTabbedPaneUI object. */ @@ -1517,8 +1798,115 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Point p = new Point(w, h); return p; } + + /** TabbedPanes in scrolling mode should use this method to + * scroll properly to the tab given by the index argument. + * + * @param index The tab to scroll to. + * @param placement The tab's placement. + */ + final void scrollTab(int index, int placement) + { + int diff; + if (index >= 0 && tabPane.isEnabledAt(index)) + { + // If the user clicked on the last tab and that one was + // only partially visible shift the scroll offset to make + // it completely visible. + switch (placement) + { + case JTabbedPane.TOP: + case JTabbedPane.BOTTOM: + if ((diff = rects[index].x + + rects[index].width + - decrButton.getX() - currentScrollOffset) > 0) + currentScrollOffset += diff; + else if ((diff = rects[index].x - currentScrollOffset) < 0) + { + if (index == 0) + currentScrollOffset = 0; + else + currentScrollOffset += diff; + } + + currentScrollLocation = tabForCoordinate(tabPane, + currentScrollOffset, + rects[index].y); + break; + default: + if ((diff = rects[index].y + rects[index].height + - decrButton.getY() - currentScrollOffset) > 0) + currentScrollOffset += diff; + else if ((diff = rects[index].y - currentScrollOffset) < 0) + { + if (index == 0) + currentScrollOffset = 0; + else + currentScrollOffset += diff; + } + + currentScrollLocation = tabForCoordinate(tabPane, + rects[index].x, + currentScrollOffset); + } + + updateViewPosition(); + updateButtons(); + } + } + + /** Sets the enabled state of the increase and decrease button + * according to the current scrolling offset and tab pane width + * (or height in TOP/BOTTOM placement). + */ + final void updateButtons() + { + int tc = tabPane.getTabCount(); + + // The increase button should be enabled as long as the + // right/bottom border of the last tab is under the left/top + // border of the decrease button. + switch (tabPane.getTabPlacement()) + { + case JTabbedPane.BOTTOM: + case JTabbedPane.TOP: + incrButton.setEnabled(currentScrollLocation + 1 < tc + && rects[tc-1].x + rects[tc-1].width + - currentScrollOffset > decrButton.getX()); + break; + default: + incrButton.setEnabled(currentScrollLocation + 1 < tc + && rects[tc-1].y + rects[tc-1].height + - currentScrollOffset > decrButton.getY()); + } + + // The decrease button is enabled when the tab pane is scrolled in any way. + decrButton.setEnabled(currentScrollOffset > 0); + + } /** + * Updates the position of the scrolling viewport's view + * according to the current scroll offset. + */ + final void updateViewPosition() + { + Point p = viewport.getViewPosition(); + + switch (tabPane.getTabPlacement()) + { + case JTabbedPane.LEFT: + case JTabbedPane.RIGHT: + p.y = currentScrollOffset; + break; + default: + p.x = currentScrollOffset; + } + + viewport.setViewPosition(p); + } + + /** * This method creates a new BasicTabbedPaneUI. * * @param c The JComponent to create a UI for. @@ -1583,22 +1971,30 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants return new TabbedPaneLayout(); else { + runCount = 1; + tabRuns[0] = 0; + incrButton = createIncreaseButton(); + incrButton.addMouseListener(mouseListener); + decrButton = createDecreaseButton(); - viewport = new ScrollingViewport(); - viewport.setLayout(null); + decrButton.addMouseListener(mouseListener); + decrButton.setEnabled(false); + panel = new ScrollingPanel(); + panel.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE); + panel.addMouseListener(mouseListener); + panel.addFocusListener(focusListener); + + viewport = new ScrollingViewport(); + viewport.setBackground(Color.LIGHT_GRAY); viewport.setView(panel); + viewport.setLayout(null); + tabPane.add(incrButton); tabPane.add(decrButton); tabPane.add(viewport); - currentScrollLocation = 0; - decrButton.setEnabled(false); - panel.addMouseListener(mouseListener); - incrButton.addMouseListener(mouseListener); - decrButton.addMouseListener(mouseListener); - viewport.setBackground(Color.LIGHT_GRAY); - + return new TabbedPaneScrollLayout(); } } @@ -1616,7 +2012,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void uninstallComponents() { - // Nothing to be done. + if (incrButton != null) + tabPane.remove(incrButton); + + if (decrButton != null) + tabPane.remove(decrButton); + + if (viewport != null) + tabPane.remove(viewport); } /** @@ -1629,8 +2032,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants "TabbedPane.font"); tabPane.setOpaque(false); - highlight = UIManager.getColor("TabbedPane.highlight"); - lightHighlight = UIManager.getColor("TabbedPane.lightHighlight"); + lightHighlight = UIManager.getColor("TabbedPane.highlight"); + highlight = UIManager.getColor("TabbedPane.light"); shadow = UIManager.getColor("TabbedPane.shadow"); darkShadow = UIManager.getColor("TabbedPane.darkShadow"); @@ -1641,10 +2044,18 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay"); tabInsets = UIManager.getInsets("TabbedPane.tabInsets"); - selectedTabPadInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabPadInsets"); + selectedTabPadInsets + = UIManager.getInsets("TabbedPane.selectedTabPadInsets"); tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets"); - contentBorderInsets = UIManager.getInsets("TabbedPane.tabbedPaneContentBorderInsets"); + contentBorderInsets + = UIManager.getInsets("TabbedPane.contentBorderInsets"); tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque"); + + // Although 'TabbedPane.contentAreaColor' is not defined in the defaults + // of BasicLookAndFeel it is used by this class. + selectedColor = UIManager.getColor("TabbedPane.contentAreaColor"); + if (selectedColor == null) + selectedColor = UIManager.getColor("control"); calcRect = new Rectangle(); tabRuns = new int[10]; @@ -1661,6 +2072,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants tabAreaRect = null; contentRect = null; tabRuns = null; + + tempIconRect = null; + tempTextRect = null; contentBorderInsets = null; tabAreaInsets = null; @@ -1672,11 +2086,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants shadow = null; lightHighlight = null; highlight = null; - - // Install UI colors and fonts. - LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background", - "TabbedPane.foreground", - "TabbedPane.font"); + + selectedColor = null; } /** @@ -1704,6 +2115,18 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants tabPane.removePropertyChangeListener(propertyChangeListener); tabPane.removeChangeListener(tabChangeListener); tabPane.removeMouseListener(mouseListener); + + if (incrButton != null) + incrButton.removeMouseListener(mouseListener); + + if (decrButton != null) + decrButton.removeMouseListener(mouseListener); + + if (panel != null) + { + panel.removeMouseListener(mouseListener); + panel.removeFocusListener(focusListener); + } focusListener = null; propertyChangeListener = null; @@ -1755,18 +2178,31 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants * This method installs keyboard actions for the JTabbedPane. */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: Implement. + InputMap keyMap = (InputMap) UIManager.get("TabbedPane.focusInputMap"); + SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, keyMap); + + keyMap = (InputMap) UIManager.get("TabbedPane.ancestorInputMap"); + SwingUtilities + .replaceUIInputMap(tabPane, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, + keyMap); + + ActionMap map = getActionMap(); + SwingUtilities.replaceUIActionMap(tabPane, map); } /** * This method uninstalls keyboard actions for the JTabbedPane. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: Implement. + SwingUtilities.replaceUIActionMap(tabPane, null); + SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, null); + SwingUtilities + .replaceUIInputMap(tabPane, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, + null); } /** @@ -1806,9 +2242,25 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPane.getTabCount() == 0) return; + + int index = tabPane.getSelectedIndex(); + if (index < 0) + index = 0; + + int tabPlacement = tabPane.getTabPlacement(); + + // Paint the tab area only in WRAP_TAB_LAYOUT Mode from this method + // because it is done through the ScrollingViewport.paint() method + // for the SCROLL_TAB_LAYOUT mode. if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) - paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); - paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); + { + g.setColor(highlight); + g.fillRect(tabAreaRect.x, tabAreaRect.y, + tabAreaRect.width, tabAreaRect.height); + paintTabArea(g, tabPlacement, index); + } + + paintContentBorder(g, tabPlacement, index); } /** @@ -1821,14 +2273,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) { - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - - boolean isScroll = tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT; - // Please note: the ordering of the painting is important. // we WANT to paint the outermost run first and then work our way in. + + // The following drawing code works for both tab layouts. int tabCount = tabPane.getTabCount(); + for (int i = runCount - 1; i >= 0; --i) { int start = tabRuns[i]; @@ -1842,14 +2292,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { if (j != selectedIndex) { - paintTab(g, tabPlacement, rects, j, ir, tr); + paintTab(g, tabPlacement, rects, j, + tempIconRect, tempTextRect); } } } - + // Paint selected tab in front of every other tab. if (selectedIndex >= 0) - paintTab(g, tabPlacement, rects, selectedIndex, ir, tr); + paintTab(g, tabPlacement, rects, selectedIndex, + tempIconRect, tempTextRect); } /** @@ -1889,8 +2341,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // Paint the text. paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title, textRect, isSelected); + // Paint icon if necessary. paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected); + // Paint focus indicator. paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect, textRect, isSelected); @@ -2030,8 +2484,17 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) { - // No reason to shift. - return 0; + switch (tabPlacement) + { + default: + case SwingUtilities.TOP: + case SwingUtilities.BOTTOM: + return 1; + case SwingUtilities.LEFT: + return (isSelected) ? -1 : 1; + case SwingUtilities.RIGHT: + return (isSelected) ? 1 : -1; + } } /** @@ -2047,8 +2510,17 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) { - // No reason to shift. - return 0; + switch (tabPlacement) + { + default: + case SwingUtilities.TOP: + return (isSelected) ? -1 : 1; + case SwingUtilities.BOTTOM: + return (isSelected) ? 1 : -1; + case SwingUtilities.LEFT: + case SwingUtilities.RIGHT: + return 0; + } } /** @@ -2078,32 +2550,33 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants g.setColor(focus); switch (tabPlacement) - { - case LEFT: - x = rect.x + 3; - y = rect.y + 3; - w = rect.width - 5; - h = rect.height - 6; - break; - case RIGHT: - x = rect.x + 2; - y = rect.y + 3; - w = rect.width - 6; - h = rect.height - 5; - break; - case BOTTOM: - x = rect.x + 3; - y = rect.y + 2; - w = rect.width - 6; - h = rect.height - 5; - break; - case TOP: - default: - x = rect.x + 3; - y = rect.y + 3; - w = rect.width - 6; - h = rect.height - 5; - } + { + case LEFT: + x = rect.x + 3; + y = rect.y + 3; + w = rect.width - 5; + h = rect.height - 6; + break; + case RIGHT: + x = rect.x + 2; + y = rect.y + 3; + w = rect.width - 6; + h = rect.height - 5; + break; + case BOTTOM: + x = rect.x + 3; + y = rect.y + 2; + w = rect.width - 6; + h = rect.height - 5; + break; + case TOP: + default: + x = rect.x + 3; + y = rect.y + 3; + w = rect.width - 6; + h = rect.height - 5; + } + BasicGraphicsUtils.drawDashedRect(g, x, y, w, h); } } @@ -2125,34 +2598,109 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { Color saved = g.getColor(); - if (! isSelected || tabPlacement != SwingConstants.TOP) - { + switch (tabPlacement) + { + case SwingConstants.TOP: g.setColor(shadow); - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); - g.setColor(darkShadow); - g.drawLine(x, y + h, x + w, y + h); - } + // Inner right line. + g.drawLine(x + w - 2, y + 2, x + w - 2, y + h); - if (! isSelected || tabPlacement != SwingConstants.LEFT) - { g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, y + h); + // Outer right line. + g.drawLine(x + w - 1, y + 2, x + w - 1, y + h); + + // Upper right corner. + g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2); + + g.setColor(lightHighlight); + + // Left line. + g.drawLine(x, y + 3, x, y + h); + + // Upper line. + g.drawLine(x + 3, y, x + w - 3, y); + + // Upper left corner. + g.drawLine(x, y + 2, x + 2, y); + + break; + case SwingConstants.LEFT: + g.setColor(lightHighlight); + // Top line. + g.drawLine(x + 3, y, x + w - 1, y); + + // Top left border. + g.drawLine(x + 2, y, x, y + 2); + + // Left line. + g.drawLine(x, y + 3, x, y + h - 4); + + // Bottom left corner. + g.drawLine(x, y + h - 3, x + 1, y + h - 2); + + g.setColor(darkShadow); + // Outer bottom line. + g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1); + g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); - } + // Inner bottom line. + g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2); + + break; + case SwingConstants.BOTTOM: + g.setColor(shadow); + // Inner right line. + g.drawLine(x + w - 2, y, x + w - 2, y + h - 2); - if (! isSelected || tabPlacement != SwingConstants.RIGHT) - { - g.setColor(lightHighlight); - g.drawLine(x, y, x, y + h); - } + // Inner bottom line. + g.drawLine(x + 2, y + h - 1, x + w - 3, y + h - 1); - if (! isSelected || tabPlacement != SwingConstants.BOTTOM) - { + g.setColor(darkShadow); + // Outer right line. + g.drawLine(x + w - 1, y, x + w - 1, y + h - 3); + + // Bottom right corner. + g.drawLine(x + w - 1, y + h - 2, x + w - 3, y + h); + + // Bottom line. + g.drawLine(x + 2, y + h, x + w - 4, y + h); + g.setColor(lightHighlight); - g.drawLine(x, y, x + w, y); - } - + // Left line. + g.drawLine(x, y, x, y + h - 3); + + // Bottom left corner. + g.drawLine(x, y + h - 2, x + 1, y + h - 1); + break; + case SwingConstants.RIGHT: + g.setColor(lightHighlight); + // Top line. + g.drawLine(x, y, x + w - 3, y); + + g.setColor(darkShadow); + // Top right corner. + g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2); + + // Outer right line. + g.drawLine(x + w - 1, y + 3, x + w - 1, y + h - 3); + + // Bottom right corner. + g.drawLine(x + w - 2, y + h - 2, x + w - 3, y + h - 1); + + // Bottom line. + g.drawLine(x, y + h - 1, x + w - 4, y + h - 1); + + g.setColor(shadow); + + // Inner right line. + g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 3); + + // Inner bottom line. + g.drawLine(x, y + h - 2, x + w - 3, y + h - 2); + + break; + } + g.setColor(saved); } @@ -2173,17 +2721,32 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants boolean isSelected) { Color saved = g.getColor(); + if (isSelected) - g.setColor(Color.LIGHT_GRAY); + g.setColor(selectedColor); else { Color bg = tabPane.getBackgroundAt(tabIndex); if (bg == null) - bg = Color.GRAY; + bg = Color.LIGHT_GRAY; g.setColor(bg); } - g.fillRect(x, y, w, h); + switch (tabPlacement) + { + case SwingConstants.TOP: + g.fillRect(x + 1, y + 1, w - 1, h - 1); + break; + case SwingConstants.BOTTOM: + g.fillRect(x, y, w - 1, h - 1); + break; + case SwingConstants.LEFT: + g.fillRect(x + 1, y + 1, w - 1, h - 2); + break; + case SwingConstants.RIGHT: + g.fillRect(x, y + 1, w - 1, h - 2); + break; + } g.setColor(saved); } @@ -2260,25 +2823,27 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Color saved = g.getColor(); g.setColor(lightHighlight); - int startgap = rects[selectedIndex].x; - int endgap = rects[selectedIndex].x + rects[selectedIndex].width; - - int diff = 0; + int startgap = rects[selectedIndex].x - currentScrollOffset; + int endgap = rects[selectedIndex].x + rects[selectedIndex].width + - currentScrollOffset; - if (tabPlacement == SwingConstants.TOP) + // Paint the highlight line with a gap if the tabs are at the top + // and the selected tab is inside the visible area. + if (tabPlacement == SwingConstants.TOP && startgap >= 0) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.x; - } - - g.drawLine(x, y, startgap - diff, y); - g.drawLine(endgap - diff, y, x + w, y); + g.drawLine(x, y, startgap, y); + g.drawLine(endgap, y, x + w - 1, y); + + g.setColor(selectedColor); + g.drawLine(startgap, y, endgap - 1, y); } else g.drawLine(x, y, x + w, y); - + + g.setColor(selectedColor); + g.drawLine(x, y + 1, x + w - 1, y + 1); + g.drawLine(x, y + 2, x + w - 1, y + 2); + g.setColor(saved); } @@ -2300,24 +2865,25 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Color saved = g.getColor(); g.setColor(lightHighlight); - int startgap = rects[selectedIndex].y; - int endgap = rects[selectedIndex].y + rects[selectedIndex].height; + int startgap = rects[selectedIndex].y - currentScrollOffset; + int endgap = rects[selectedIndex].y + rects[selectedIndex].height + - currentScrollOffset; int diff = 0; - if (tabPlacement == SwingConstants.LEFT) + if (tabPlacement == SwingConstants.LEFT && startgap >= 0) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.y; - } - - g.drawLine(x, y, x, startgap - diff); - g.drawLine(x, endgap - diff, x, y + h); + g.drawLine(x, y, x, startgap); + g.drawLine(x, endgap, x, y + h - 1); + + g.setColor(selectedColor); + g.drawLine(x, startgap, x, endgap - 1); } else - g.drawLine(x, y, x, y + h); + g.drawLine(x, y, x, y + h - 1); + + g.setColor(selectedColor); + g.drawLine(x + 1, y + 1, x + 1, y + h - 4); g.setColor(saved); } @@ -2339,34 +2905,34 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { Color saved = g.getColor(); - int startgap = rects[selectedIndex].x; - int endgap = rects[selectedIndex].x + rects[selectedIndex].width; - - int diff = 0; + int startgap = rects[selectedIndex].x - currentScrollOffset; + int endgap = rects[selectedIndex].x + rects[selectedIndex].width + - currentScrollOffset; - if (tabPlacement == SwingConstants.BOTTOM) + if (tabPlacement == SwingConstants.BOTTOM && startgap >= 0) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.x; - } - g.setColor(shadow); - g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1); - g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1); + g.drawLine(x + 1, y + h - 2, startgap, y + h - 2); + g.drawLine(endgap, y + h - 2, x + w - 2, y + h - 2); g.setColor(darkShadow); - g.drawLine(x, y + h, startgap - diff, y + h); - g.drawLine(endgap - diff, y + h, x + w, y + h); + g.drawLine(x, y + h - 1, startgap , y + h - 1); + g.drawLine(endgap, y + h - 1, x + w - 1, y + h - 1); + + g.setColor(selectedColor); + g.drawLine(startgap, y + h - 1, endgap - 1, y + h - 1); + g.drawLine(startgap, y + h - 2, endgap - 1, y + h - 2); } else { g.setColor(shadow); - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + g.drawLine(x + 1, y + h - 2, x + w - 1, y + h - 2); g.setColor(darkShadow); - g.drawLine(x, y + h, x + w, y + h); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); } + + g.setColor(selectedColor); + g.drawLine(x + 1, y + h - 3, x + w - 2, y + h - 3); g.setColor(saved); } @@ -2387,34 +2953,36 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int w, int h) { Color saved = g.getColor(); - int startgap = rects[selectedIndex].y; - int endgap = rects[selectedIndex].y + rects[selectedIndex].height; + int startgap = rects[selectedIndex].y - currentScrollOffset; + int endgap = rects[selectedIndex].y + rects[selectedIndex].height + - currentScrollOffset; int diff = 0; - if (tabPlacement == SwingConstants.RIGHT) + if (tabPlacement == SwingConstants.RIGHT && startgap >= 0) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.y; - } - g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff); - g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1); + g.drawLine(x + w - 2, y + 1, x + w - 2, startgap); + g.drawLine(x + w - 2, endgap, x + w - 2, y + h - 2); g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, startgap - diff); - g.drawLine(x + w, endgap - diff, x + w, y + h); + g.drawLine(x + w - 1, y, x + w - 1, startgap); + g.drawLine(x + w - 1, endgap, x + w - 1, y + h - 2); + + g.setColor(selectedColor); + g.drawLine(x + w - 2, startgap, x + w - 2, endgap - 1); + g.drawLine(x + w - 1, startgap, x + w - 1, endgap - 1); } else { g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2); g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, y + h); + g.drawLine(x + w - 1, y, x + w - 1, y + h - 2); } + + g.setColor(selectedColor); + g.drawLine(x + w - 3, y + 1, x + w - 3, y + h - 4); g.setColor(saved); } @@ -2458,11 +3026,15 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public int tabForCoordinate(JTabbedPane pane, int x, int y) { + // Note: This code is tab layout mode agnostic. if (! tabPane.isValid()) tabPane.validate(); - + int tabCount = tabPane.getTabCount(); - int index = -1; + + // If the user clicked outside of any tab rect the + // selection should not change. + int index = tabPane.getSelectedIndex(); for (int i = 0; i < tabCount; ++i) { if (rects[i].contains(x, y)) @@ -2472,8 +3044,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants } } - // FIXME: Handle scrollable tab layout. - return index; } @@ -2569,7 +3139,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int getRunForTab(int tabCount, int tabIndex) { if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0) - return 1; + return 0; for (int i = 0; i < runCount; i++) { int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; @@ -2688,6 +3258,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected View getTextViewForTab(int tabIndex) { + // FIXME: When the label contains HTML this should return something + // non-null. return null; } @@ -2704,7 +3276,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) { - // FIXME: Handle HTML somehow. + // FIXME: Handle HTML by using the view (see getTextViewForTab). int height = fontHeight; Icon icon = getIconForTab(tabIndex); @@ -2921,8 +3493,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), tabPane.getSelectedIndex(), - (tabPlacement == SwingConstants.RIGHT) - ? true : false); + (tabPlacement == SwingConstants.TOP) + ? direction == SwingConstants.NORTH + : direction == SwingConstants.SOUTH); selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), offset); } @@ -2938,8 +3511,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), tabPane.getSelectedIndex(), - (tabPlacement == SwingConstants.RIGHT) - ? true : false); + (tabPlacement == SwingConstants.LEFT) + ? direction == SwingConstants.WEST + : direction == SwingConstants.EAST); selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), offset); } @@ -2953,8 +3527,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void selectNextTabInRun(int current) { - tabPane.setSelectedIndex(getNextTabIndexInRun(tabPane.getTabCount(), - current)); + current = getNextTabIndexInRun(tabPane.getTabCount(), + current); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + scrollTab(current, tabPane.getTabPlacement()); + + tabPane.setSelectedIndex(current); } /** @@ -2964,8 +3543,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void selectPreviousTabInRun(int current) { - tabPane.setSelectedIndex(getPreviousTabIndexInRun(tabPane.getTabCount(), - current)); + current = getPreviousTabIndexInRun(tabPane.getTabCount(), + current); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + scrollTab(current, tabPane.getTabPlacement()); + + tabPane.setSelectedIndex(current); } /** @@ -2975,7 +3559,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void selectNextTab(int current) { - tabPane.setSelectedIndex(getNextTabIndex(current)); + current = getNextTabIndex(current); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + scrollTab(current, tabPane.getTabPlacement()); + + tabPane.setSelectedIndex(current); } /** @@ -2985,7 +3574,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void selectPreviousTab(int current) { - tabPane.setSelectedIndex(getPreviousTabIndex(current)); + current = getPreviousTabIndex(current); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + scrollTab(current, tabPane.getTabPlacement()); + + tabPane.setSelectedIndex(current); } /** @@ -3019,7 +3613,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int index = tabForCoordinate(tabPane, x, y); if (index != -1) - tabPane.setSelectedIndex(index); + { + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + scrollTab(index, tabPlacement); + tabPane.setSelectedIndex(index); + } } // This method is called when you press up/down to cycle through tab runs. @@ -3056,6 +3654,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants else offset = rects[lastTabInRun(tabCount, nextRun)].x - rects[lastTabInRun(tabCount, currRun)].x; + return offset; } @@ -3102,9 +3701,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { int index = getNextTabIndex(base); int run = getRunForTab(tabCount, base); - if (index == lastTabInRun(tabCount, run) + 1) - index = lastTabInRun(tabCount, getPreviousTabRun(run)) + 1; - return getNextTabIndex(base); + if (base == lastTabInRun(tabCount, run)) + index = (run > 0) + ? lastTabInRun(tabCount, getPreviousTabRun(run)) + 1 + : 0; + + return index; } /** @@ -3122,7 +3724,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int run = getRunForTab(tabCount, base); if (index == lastTabInRun(tabCount, getPreviousTabRun(run))) index = lastTabInRun(tabCount, run); - return getPreviousTabIndex(base); + + return index; } /** @@ -3180,6 +3783,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // so I won't check it either. switch (targetPlacement) { + default: case SwingConstants.TOP: targetInsets.top = topInsets.top; targetInsets.left = topInsets.left; @@ -3206,6 +3810,44 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants break; } } + + ActionMap getActionMap() + { + ActionMap map = (ActionMap) UIManager.get("TabbedPane.actionMap"); + + if (map == null) // first time here + { + map = createActionMap(); + if (map != null) + UIManager.put("TabbedPane.actionMap", map); + } + return map; + } + + ActionMap createActionMap() + { + ActionMap map = new ActionMapUIResource(); + + map.put("navigatePageDown", new NavigatePageDownAction()); + map.put("navigatePageUp", new NavigatePageUpAction()); + map.put("navigateDown", + new NavigateAction("navigateDown", SwingConstants.SOUTH)); + + map.put("navigateUp", + new NavigateAction("navigateUp", SwingConstants.NORTH)); + + map.put("navigateLeft", + new NavigateAction("navigateLeft", SwingConstants.WEST)); + + map.put("navigateRight", + new NavigateAction("navigateRight", SwingConstants.EAST)); + + map.put("requestFocusForVisibleComponent", + new RequestFocusForVisibleComponentAction()); + map.put("requestFocus", new RequestFocusAction()); + + return map; + } /** * Sets the tab which should be highlighted when in rollover mode. And diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java index cdd44a711e7..15be4d57e62 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Color; import java.awt.Component; import java.awt.ComponentOrientation; @@ -48,7 +46,6 @@ import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; @@ -58,6 +55,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; +import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.CellRendererPane; import javax.swing.DefaultCellEditor; @@ -65,16 +63,16 @@ import javax.swing.DefaultListSelectionModel; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JTable; -import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.ChangeEvent; import javax.swing.event.MouseInputListener; import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TableUI; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; @@ -164,14 +162,37 @@ public class BasicTableUI extends TableUI public class FocusHandler implements FocusListener { - public void focusGained(FocusEvent e) + public void focusGained(FocusEvent e) { - // TODO: Implement this properly. + // The only thing that is affected by a focus change seems to be + // how the lead cell is painted. So we repaint this cell. + repaintLeadCell(); } - public void focusLost(FocusEvent e) + public void focusLost(FocusEvent e) + { + // The only thing that is affected by a focus change seems to be + // how the lead cell is painted. So we repaint this cell. + repaintLeadCell(); + } + + /** + * Repaints the lead cell in response to a focus change, to refresh + * the display of the focus indicator. + */ + private void repaintLeadCell() { - // TODO: Implement this properly. + int rowCount = table.getRowCount(); + int columnCount = table.getColumnCount(); + int rowLead = table.getSelectionModel().getLeadSelectionIndex(); + int columnLead = table.getColumnModel().getSelectionModel(). + getLeadSelectionIndex(); + if (rowLead >= 0 && rowLead < rowCount && columnLead >= 0 + && columnLead < columnCount) + { + Rectangle dirtyRect = table.getCellRect(rowLead, columnLead, false); + table.repaint(dirtyRect); + } } } @@ -242,19 +263,19 @@ public class BasicTableUI extends TableUI } } - public void mouseEntered(MouseEvent e) + public void mouseEntered(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } - public void mouseExited(MouseEvent e) + public void mouseExited(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } - public void mouseMoved(MouseEvent e) + public void mouseMoved(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } public void mousePressed(MouseEvent e) @@ -287,6 +308,9 @@ public class BasicTableUI extends TableUI colLead != colModel.getLeadSelectionIndex()) if (table.isEditing()) table.editingStopped(new ChangeEvent(e)); + + // Must request focus explicitly. + table.requestFocusInWindow(); } } @@ -456,66 +480,100 @@ public class BasicTableUI extends TableUI table.setOpaque(true); } + /** + * Installs keyboard actions on the table. + */ protected void installKeyboardActions() { - InputMap ancestorMap = (InputMap) UIManager.get("Table.ancestorInputMap"); - InputMapUIResource parentInputMap = new InputMapUIResource(); - // FIXME: The JDK uses a LazyActionMap for parentActionMap - ActionMap parentActionMap = new ActionMapUIResource(); - action = new TableAction(); - Object keys[] = ancestorMap.allKeys(); - // Register key bindings in the UI InputMap-ActionMap pair - for (int i = 0; i < keys.length; i++) - { - KeyStroke stroke = (KeyStroke) keys[i]; - String actionString = (String) ancestorMap.get(stroke); + // Install the input map. + InputMap inputMap = + (InputMap) SharedUIDefaults.get("Table.ancestorInputMap"); + SwingUtilities.replaceUIInputMap(table, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, + inputMap); - parentInputMap.put(KeyStroke.getKeyStroke(stroke.getKeyCode(), - stroke.getModifiers()), - actionString); - - parentActionMap.put(actionString, - new ActionListenerProxy(action, actionString)); + // FIXME: The JDK uses a LazyActionMap for parentActionMap + SwingUtilities.replaceUIActionMap(table, getActionMap()); - } - // Set the UI InputMap-ActionMap pair to be the parents of the - // JTable's InputMap-ActionMap pair - parentInputMap.setParent(table.getInputMap( - JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).getParent()); - parentActionMap.setParent(table.getActionMap().getParent()); - table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT). - setParent(parentInputMap); - table.getActionMap().setParent(parentActionMap); } /** - * This class is used to mimmic the behaviour of the JDK when registering - * keyboard actions. It is the same as the private class used in JComponent - * for the same reason. This class receives an action event and dispatches - * it to the true receiver after altering the actionCommand property of the - * event. + * Fetches the action map from the UI defaults, or create a new one + * if the action map hasn't been initialized. + * + * @return the action map */ - private static class ActionListenerProxy - extends AbstractAction + private ActionMap getActionMap() { - ActionListener target; - String bindingCommandName; - - public ActionListenerProxy(ActionListener li, - String cmd) - { - target = li; - bindingCommandName = cmd; - } + ActionMap am = (ActionMap) UIManager.get("Table.actionMap"); + if (am == null) + { + am = createDefaultActions(); + UIManager.getLookAndFeelDefaults().put("Table.actionMap", am); + } + return am; + } - public void actionPerformed(ActionEvent e) - { - ActionEvent derivedEvent = new ActionEvent(e.getSource(), - e.getID(), - bindingCommandName, - e.getModifiers()); - target.actionPerformed(derivedEvent); - } + private ActionMap createDefaultActions() + { + ActionMapUIResource am = new ActionMapUIResource(); + Action action = new TableAction(); + + am.put("cut", TransferHandler.getCutAction()); + am.put("copy", TransferHandler.getCopyAction()); + am.put("paste", TransferHandler.getPasteAction()); + + am.put("cancel", action); + am.put("selectAll", action); + am.put("clearSelection", action); + am.put("startEditing", action); + + am.put("selectNextRow", action); + am.put("selectNextRowCell", action); + am.put("selectNextRowExtendSelection", action); + am.put("selectNextRowChangeLead", action); + + am.put("selectPreviousRow", action); + am.put("selectPreviousRowCell", action); + am.put("selectPreviousRowExtendSelection", action); + am.put("selectPreviousRowChangeLead", action); + + am.put("selectNextColumn", action); + am.put("selectNextColumnCell", action); + am.put("selectNextColumnExtendSelection", action); + am.put("selectNextColumnChangeLead", action); + + am.put("selectPreviousColumn", action); + am.put("selectPreviousColumnCell", action); + am.put("selectPreviousColumnExtendSelection", action); + am.put("selectPreviousColumnChangeLead", action); + + am.put("scrollLeftChangeSelection", action); + am.put("scrollLeftExtendSelection", action); + am.put("scrollRightChangeSelection", action); + am.put("scrollRightExtendSelection", action); + + am.put("scrollUpChangeSelection", action); + am.put("scrollUpExtendSelection", action); + am.put("scrollDownChangeSelection", action); + am.put("scrolldownExtendSelection", action); + + am.put("selectFirstColumn", action); + am.put("selectFirstColumnExtendSelection", action); + am.put("selectLastColumn", action); + am.put("selectLastColumnExtendSelection", action); + + am.put("selectFirstRow", action); + am.put("selectFirstRowExtendSelection", action); + am.put("selectLastRow", action); + am.put("selectLastRowExtendSelection", action); + + am.put("addToSelection", action); + am.put("toggleAndAnchor", action); + am.put("extendTo", action); + am.put("moveSelectionTo", action); + + return am; } /** @@ -524,7 +582,8 @@ public class BasicTableUI extends TableUI * method is called when a key that has been registered for the JTable * is received. */ - class TableAction extends AbstractAction + private static class TableAction + extends AbstractAction { /** * What to do when this action is called. @@ -533,6 +592,8 @@ public class BasicTableUI extends TableUI */ public void actionPerformed(ActionEvent e) { + JTable table = (JTable) e.getSource(); + DefaultListSelectionModel rowModel = (DefaultListSelectionModel) table.getSelectionModel(); DefaultListSelectionModel colModel @@ -543,9 +604,11 @@ public class BasicTableUI extends TableUI int colLead = colModel.getLeadSelectionIndex(); int colMax = table.getModel().getColumnCount() - 1; - - String command = e.getActionCommand(); - + + // The command with which the action has been called is stored + // in this undocumented action value. This allows us to have only + // one Action instance to serve all keyboard input for JTable. + String command = (String) getValue("__command__"); if (command.equals("selectPreviousRowExtendSelection")) { rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0)); @@ -603,11 +666,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollUpExtendSelection")) { int target; - if (rowLead == getFirstVisibleRowIndex()) - target = Math.max(0, rowLead - (getLastVisibleRowIndex() - - getFirstVisibleRowIndex() + 1)); + if (rowLead == getFirstVisibleRowIndex(table)) + target = Math.max(0, rowLead - (getLastVisibleRowIndex(table) + - getFirstVisibleRowIndex(table) + 1)); else - target = getFirstVisibleRowIndex(); + target = getFirstVisibleRowIndex(table); rowModel.setLeadSelectionIndex(target); colModel.setLeadSelectionIndex(colLead); @@ -620,11 +683,12 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollRightChangeSelection")) { int target; - if (colLead == getLastVisibleColumnIndex()) - target = Math.min(colMax, colLead + (getLastVisibleColumnIndex() - - getFirstVisibleColumnIndex() + 1)); + if (colLead == getLastVisibleColumnIndex(table)) + target = Math.min(colMax, colLead + + (getLastVisibleColumnIndex(table) + - getFirstVisibleColumnIndex(table) + 1)); else - target = getLastVisibleColumnIndex(); + target = getLastVisibleColumnIndex(table); colModel.setSelectionInterval(target, target); rowModel.setSelectionInterval(rowLead, rowLead); @@ -637,11 +701,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollLeftChangeSelection")) { int target; - if (colLead == getFirstVisibleColumnIndex()) - target = Math.max(0, colLead - (getLastVisibleColumnIndex() - - getFirstVisibleColumnIndex() + 1)); + if (colLead == getFirstVisibleColumnIndex(table)) + target = Math.max(0, colLead - (getLastVisibleColumnIndex(table) + - getFirstVisibleColumnIndex(table) + 1)); else - target = getFirstVisibleColumnIndex(); + target = getFirstVisibleColumnIndex(table); colModel.setSelectionInterval(target, target); rowModel.setSelectionInterval(rowLead, rowLead); @@ -723,14 +787,18 @@ public class BasicTableUI extends TableUI // If there are multiple rows and columns selected, select the next // cell and wrap at the edges of the selection. if (command.indexOf("Column") != -1) - advanceMultipleSelection(colModel, colMinSelected, colMaxSelected, - rowModel, rowMinSelected, rowMaxSelected, - command.equals("selectPreviousColumnCell"), true); + advanceMultipleSelection(table, colModel, colMinSelected, + colMaxSelected, rowModel, rowMinSelected, + rowMaxSelected, + command.equals("selectPreviousColumnCell"), + true); else - advanceMultipleSelection(rowModel, rowMinSelected, rowMaxSelected, - colModel, colMinSelected, colMaxSelected, - command.equals("selectPreviousRowCell"), false); + advanceMultipleSelection(table, rowModel, rowMinSelected, + rowMaxSelected, colModel, colMinSelected, + colMaxSelected, + command.equals("selectPreviousRowCell"), + false); } else if (command.equals("selectNextColumn")) { @@ -740,11 +808,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollLeftExtendSelection")) { int target; - if (colLead == getFirstVisibleColumnIndex()) - target = Math.max(0, colLead - (getLastVisibleColumnIndex() - - getFirstVisibleColumnIndex() + 1)); + if (colLead == getFirstVisibleColumnIndex(table)) + target = Math.max(0, colLead - (getLastVisibleColumnIndex(table) + - getFirstVisibleColumnIndex(table) + 1)); else - target = getFirstVisibleColumnIndex(); + target = getFirstVisibleColumnIndex(table); colModel.setLeadSelectionIndex(target); rowModel.setLeadSelectionIndex(rowLead); @@ -752,11 +820,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollDownChangeSelection")) { int target; - if (rowLead == getLastVisibleRowIndex()) - target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex() - - getFirstVisibleRowIndex() + 1)); + if (rowLead == getLastVisibleRowIndex(table)) + target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table) + - getFirstVisibleRowIndex(table) + 1)); else - target = getLastVisibleRowIndex(); + target = getLastVisibleRowIndex(table); rowModel.setSelectionInterval(target, target); colModel.setSelectionInterval(colLead, colLead); @@ -764,11 +832,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollRightExtendSelection")) { int target; - if (colLead == getLastVisibleColumnIndex()) - target = Math.min(colMax, colLead + (getLastVisibleColumnIndex() - - getFirstVisibleColumnIndex() + 1)); + if (colLead == getLastVisibleColumnIndex(table)) + target = Math.min(colMax, colLead + (getLastVisibleColumnIndex(table) + - getFirstVisibleColumnIndex(table) + 1)); else - target = getLastVisibleColumnIndex(); + target = getLastVisibleColumnIndex(table); colModel.setLeadSelectionIndex(target); rowModel.setLeadSelectionIndex(rowLead); @@ -785,11 +853,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollDownExtendSelection")) { int target; - if (rowLead == getLastVisibleRowIndex()) - target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex() - - getFirstVisibleRowIndex() + 1)); + if (rowLead == getLastVisibleRowIndex(table)) + target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table) + - getFirstVisibleRowIndex(table) + 1)); else - target = getLastVisibleRowIndex(); + target = getLastVisibleRowIndex(table); rowModel.setLeadSelectionIndex(target); colModel.setLeadSelectionIndex(colLead); @@ -797,11 +865,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollUpChangeSelection")) { int target; - if (rowLead == getFirstVisibleRowIndex()) - target = Math.max(0, rowLead - (getLastVisibleRowIndex() - - getFirstVisibleRowIndex() + 1)); + if (rowLead == getFirstVisibleRowIndex(table)) + target = Math.max(0, rowLead - (getLastVisibleRowIndex(table) + - getFirstVisibleRowIndex(table) + 1)); else - target = getFirstVisibleRowIndex(); + target = getFirstVisibleRowIndex(table); rowModel.setSelectionInterval(target, target); colModel.setSelectionInterval(colLead, colLead); @@ -926,7 +994,7 @@ public class BasicTableUI extends TableUI * Returns the column index of the first visible column. * @return the column index of the first visible column. */ - int getFirstVisibleColumnIndex() + int getFirstVisibleColumnIndex(JTable table) { ComponentOrientation or = table.getComponentOrientation(); Rectangle r = table.getVisibleRect(); @@ -939,7 +1007,7 @@ public class BasicTableUI extends TableUI * Returns the column index of the last visible column. * */ - int getLastVisibleColumnIndex() + int getLastVisibleColumnIndex(JTable table) { ComponentOrientation or = table.getComponentOrientation(); Rectangle r = table.getVisibleRect(); @@ -952,7 +1020,7 @@ public class BasicTableUI extends TableUI * Returns the row index of the first visible row. * */ - int getFirstVisibleRowIndex() + int getFirstVisibleRowIndex(JTable table) { ComponentOrientation or = table.getComponentOrientation(); Rectangle r = table.getVisibleRect(); @@ -965,7 +1033,7 @@ public class BasicTableUI extends TableUI * Returns the row index of the last visible row. * */ - int getLastVisibleRowIndex() + int getLastVisibleRowIndex(JTable table) { ComponentOrientation or = table.getComponentOrientation(); Rectangle r = table.getVisibleRect(); @@ -977,7 +1045,7 @@ public class BasicTableUI extends TableUI // area is larger than the table) if (table.rowAtPoint(r.getLocation()) == -1) { - if (getFirstVisibleRowIndex() == -1) + if (getFirstVisibleRowIndex(table) == -1) return -1; else return table.getModel().getRowCount() - 1; @@ -1003,7 +1071,8 @@ public class BasicTableUI extends TableUI * @param reverse true if shift was held for the event * @param eventIsTab true if TAB was pressed, false if ENTER pressed */ - void advanceMultipleSelection(ListSelectionModel firstModel, int firstMin, + void advanceMultipleSelection(JTable table, ListSelectionModel firstModel, + int firstMin, int firstMax, ListSelectionModel secondModel, int secondMin, int secondMax, boolean reverse, boolean eventIsTab) @@ -1167,30 +1236,24 @@ public class BasicTableUI extends TableUI table.addPropertyChangeListener(propertyChangeListener); } - protected void uninstallDefaults() + /** + * Uninstalls UI defaults that have been installed by + * {@link #installDefaults()}. + */ + protected void uninstallDefaults() { - // TODO: this method used to do the following which is not - // quite right (at least it breaks apps that run fine with the - // JDK): - // - // table.setFont(null); - // table.setGridColor(null); - // table.setForeground(null); - // table.setBackground(null); - // table.setSelectionForeground(null); - // table.setSelectionBackground(null); - // - // This would leave the component in a corrupt state, which is - // not acceptable. A possible solution would be to have component - // level defaults installed, that get overridden by the UI defaults - // and get restored in this method. I am not quite sure about this - // though. / Roman Kennke + // Nothing to do here for now. } - protected void uninstallKeyboardActions() - throws NotImplementedException + /** + * Uninstalls the keyboard actions that have been installed by + * {@link #installKeyboardActions()}. + */ + protected void uninstallKeyboardActions() { - // TODO: Implement this properly. + SwingUtilities.replaceUIInputMap(table, JComponent. + WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); + SwingUtilities.replaceUIActionMap(table, null); } protected void uninstallListeners() diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java index 89c4e5a7562..6792aa065da 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.beans.PropertyChangeEvent; import javax.swing.JComponent; @@ -94,12 +95,24 @@ public class BasicTextFieldUI extends BasicTextUI { if (event.getPropertyName().equals("editable")) { - boolean editable = ((Boolean) event.getNewValue()).booleanValue(); - // Changing the color only if the current background is an instance of // ColorUIResource is the behavior of the RI. if (textComponent.getBackground() instanceof ColorUIResource) - textComponent.setBackground(editable ? background : inactiveBackground); + { + Color c = null; + Color old = textComponent.getBackground(); + String prefix = getPropertyPrefix(); + if (! textComponent.isEnabled()) + c = SharedUIDefaults.getColor(prefix + ".disabledBackground"); + if (c == null && ! textComponent.isEditable()) + c = SharedUIDefaults.getColor(prefix + ".inactiveBackground"); + if (c == null) + c = SharedUIDefaults.getColor(prefix + ".background"); + if (c != null && c != old) + { + textComponent.setBackground(c); + } + } } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java index 34261cfe644..8e9c8c949f3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java @@ -83,7 +83,6 @@ import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; import javax.swing.text.Keymap; import javax.swing.text.Position; -import javax.swing.text.Utilities; import javax.swing.text.View; import javax.swing.text.ViewFactory; @@ -418,7 +417,19 @@ public abstract class BasicTextUI extends TextUI if (event.getPropertyName().equals("document")) { // Document changed. - modelChanged(); + Object oldValue = event.getOldValue(); + if (oldValue != null) + { + Document oldDoc = (Document) oldValue; + oldDoc.removeDocumentListener(documentHandler); + } + Object newValue = event.getNewValue(); + if (newValue != null) + { + Document newDoc = (Document) newValue; + newDoc.addDocumentListener(documentHandler); + } + modelChanged(); } BasicTextUI.this.propertyChange(event); @@ -501,18 +512,6 @@ public abstract class BasicTextUI extends TextUI DocumentHandler documentHandler = new DocumentHandler(); /** - * The standard background color. This is the color which is used to paint - * text in enabled text components. - */ - Color background; - - /** - * The inactive background color. This is the color which is used to paint - * text in disabled text components. - */ - Color inactiveBackground; - - /** * Creates a new <code>BasicTextUI</code> instance. */ public BasicTextUI() @@ -558,22 +557,23 @@ public abstract class BasicTextUI extends TextUI */ public void installUI(final JComponent c) { - super.installUI(c); - textComponent = (JTextComponent) c; + installDefaults(); + textComponent.addPropertyChangeListener(updateHandler); Document doc = textComponent.getDocument(); if (doc == null) { doc = getEditorKit(textComponent).createDefaultDocument(); textComponent.setDocument(doc); } - installDefaults(); + else + { + doc.addDocumentListener(documentHandler); + modelChanged(); + } + installListeners(); installKeyboardActions(); - - // We need to trigger this so that the view hierarchy gets initialized. - modelChanged(); - } /** @@ -581,34 +581,60 @@ public abstract class BasicTextUI extends TextUI */ protected void installDefaults() { + String prefix = getPropertyPrefix(); + // Install the standard properties. + LookAndFeel.installColorsAndFont(textComponent, prefix + ".background", + prefix + ".foreground", prefix + ".font"); + LookAndFeel.installBorder(textComponent, prefix + ".border"); + textComponent.setMargin(UIManager.getInsets(prefix + ".margin")); + + // Some additional text component only properties. + Color color = textComponent.getCaretColor(); + if (color == null || color instanceof UIResource) + { + color = UIManager.getColor(prefix + ".caretForeground"); + textComponent.setCaretColor(color); + } + + // Fetch the colors for enabled/disabled text components. + color = textComponent.getDisabledTextColor(); + if (color == null || color instanceof UIResource) + { + color = UIManager.getColor(prefix + ".inactiveBackground"); + textComponent.setDisabledTextColor(color); + } + color = textComponent.getSelectedTextColor(); + if (color == null || color instanceof UIResource) + { + color = UIManager.getColor(prefix + ".selectionForeground"); + textComponent.setSelectedTextColor(color); + } + color = textComponent.getSelectionColor(); + if (color == null || color instanceof UIResource) + { + color = UIManager.getColor(prefix + ".selectionBackground"); + textComponent.setSelectionColor(color); + } + + Insets margin = textComponent.getMargin(); + if (margin == null || margin instanceof UIResource) + { + margin = UIManager.getInsets(prefix + ".margin"); + textComponent.setMargin(margin); + } + Caret caret = textComponent.getCaret(); - if (caret == null) + if (caret == null || caret instanceof UIResource) { caret = createCaret(); textComponent.setCaret(caret); + caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate")); } Highlighter highlighter = textComponent.getHighlighter(); - if (highlighter == null) + if (highlighter == null || highlighter instanceof UIResource) textComponent.setHighlighter(createHighlighter()); - String prefix = getPropertyPrefix(); - LookAndFeel.installColorsAndFont(textComponent, prefix + ".background", - prefix + ".foreground", prefix + ".font"); - LookAndFeel.installBorder(textComponent, prefix + ".border"); - textComponent.setMargin(UIManager.getInsets(prefix + ".margin")); - - caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate")); - - // Fetch the colors for enabled/disabled text components. - background = UIManager.getColor(prefix + ".background"); - inactiveBackground = UIManager.getColor(prefix + ".inactiveBackground"); - textComponent.setDisabledTextColor(UIManager.getColor(prefix - + ".inactiveForeground")); - textComponent.setSelectedTextColor(UIManager.getColor(prefix - + ".selectionForeground")); - textComponent.setSelectionColor(UIManager.getColor(prefix - + ".selectionBackground")); } /** @@ -670,18 +696,6 @@ public abstract class BasicTextUI extends TextUI protected void installListeners() { textComponent.addFocusListener(focuslistener); - textComponent.addPropertyChangeListener(updateHandler); - installDocumentListeners(); - } - - /** - * Installs the document listeners on the textComponent's model. - */ - private void installDocumentListeners() - { - Document doc = textComponent.getDocument(); - if (doc != null) - doc.addDocumentListener(documentHandler); } /** @@ -845,9 +859,7 @@ public abstract class BasicTextUI extends TextUI */ protected void uninstallListeners() { - textComponent.removePropertyChangeListener(updateHandler); textComponent.removeFocusListener(focuslistener); - textComponent.getDocument().removeDocumentListener(documentHandler); } /** @@ -1029,7 +1041,7 @@ public abstract class BasicTextUI extends TextUI */ public void damageRange(JTextComponent t, int p0, int p1) { - damageRange(t, p0, p1, null, null); + damageRange(t, p0, p1, Position.Bias.Forward, Position.Bias.Backward); } /** @@ -1049,106 +1061,36 @@ public abstract class BasicTextUI extends TextUI public void damageRange(JTextComponent t, int p0, int p1, Position.Bias firstBias, Position.Bias secondBias) { - // Do nothing if the component cannot be properly displayed. - if (t.getWidth() == 0 || t.getHeight() == 0) - return; - - try + Rectangle alloc = getVisibleEditorRect(); + if (alloc != null) { - // Limit p0 and p1 to sane values to prevent unfriendly - // BadLocationExceptions. This makes it possible for the highlighter - // to send us illegal values which can happen when a large number - // of selected characters are removed (eg. by pressing delete - // or backspace). - // The reference implementation does not throw an exception, too. - p0 = Math.min(p0, t.getDocument().getLength()); - p1 = Math.min(p1, t.getDocument().getLength()); - - Rectangle l1 = modelToView(t, p0, firstBias); - Rectangle l2 = modelToView(t, p1, secondBias); - if (l1 == null || l2 == null) + Document doc = t.getDocument(); + + // Acquire lock here to avoid structural changes in between. + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readLock(); + try { - // Unable to determine the start or end of the selection. - t.repaint(); + rootView.setSize(alloc.width, alloc.height); + Shape damage = rootView.modelToView(p0, firstBias, p1, secondBias, + alloc); + Rectangle r = damage instanceof Rectangle ? (Rectangle) damage + : damage.getBounds(); + textComponent.repaint(r.x, r.y, r.width, r.height); } - else if (l1.y == l2.y) + catch (BadLocationException ex) { - SwingUtilities.computeUnion(l2.x, l2.y, l2.width, l2.height, l1); - t.repaint(l1); + // Lets ignore this as it causes no serious problems. + // For debugging, comment this out. + // ex.printStackTrace(); } - else + finally { - // The two rectangles lie on different lines and we need a - // different algorithm to calculate the damaged area: - // 1. The line of p0 is damaged from the position of p0 - // to the right border. - // 2. All lines between the ones where p0 and p1 lie on - // are completely damaged. Use the allocation area to find - // out the bounds. - // 3. The final line is damaged from the left bound to the - // position of p1. - Insets insets = t.getInsets(); - - // Damage first line until the end. - l1.width = insets.right + t.getWidth() - l1.x; - t.repaint(l1); - - // Note: Utilities.getPositionBelow() may return the offset - // that was put in. In that case there is no next line and - // we should stop searching for one. - - int posBelow = Utilities.getPositionBelow(t, p0, l1.x); - int p1RowStart = Utilities.getRowStart(t, p1); - - if (posBelow != -1 - && posBelow != p0 - && Utilities.getRowStart(t, posBelow) != p1RowStart) - { - // Take the rectangle of the offset we just found and grow it - // to the maximum width. Retain y because this is our start - // height. - Rectangle grow = modelToView(t, posBelow); - grow.x = insets.left; - grow.width = t.getWidth() + insets.right; - - // Find further lines which have to be damaged completely. - int nextPosBelow = posBelow; - while (nextPosBelow != -1 - && posBelow != nextPosBelow - && Utilities.getRowStart(t, nextPosBelow) != p1RowStart) - { - posBelow = nextPosBelow; - nextPosBelow = Utilities.getPositionBelow(t, posBelow, - l1.x); - - if (posBelow == nextPosBelow) - break; - } - // Now posBelow is an offset on the last line which has to be - // damaged completely. (newPosBelow is on the same line as p1) - - // Retrieve the rectangle of posBelow and use its y and height - // value to calculate the final height of the multiple line - // spanning rectangle. - Rectangle end = modelToView(t, posBelow); - grow.height = end.y + end.height - grow.y; - - // Mark that area as damage. - t.repaint(grow); - } - - // Damage last line from its beginning to the position of p1. - l2.width += l2.x; - l2.x = insets.left; - t.repaint(l2); + // Release lock. + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readUnlock(); } } - catch (BadLocationException ex) - { - AssertionError err = new AssertionError("Unexpected bad location"); - err.initCause(ex); - throw err; - } } /** @@ -1245,10 +1187,29 @@ public abstract class BasicTextUI extends TextUI public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias) throws BadLocationException { - Rectangle r = getVisibleEditorRect(); - - return (r != null) ? rootView.modelToView(pos, r, bias).getBounds() - : null; + // We need to read-lock here because we depend on the document + // structure not beeing changed in between. + Document doc = textComponent.getDocument(); + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readLock(); + Rectangle rect = null; + try + { + Rectangle r = getVisibleEditorRect(); + if (r != null) + { + rootView.setSize(r.width, r.height); + Shape s = rootView.modelToView(pos, r, bias); + if (s != null) + rect = s.getBounds(); + } + } + finally + { + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readUnlock(); + } + return rect; } /** @@ -1361,7 +1322,6 @@ public abstract class BasicTextUI extends TextUI Document doc = textComponent.getDocument(); if (doc == null) return; - installDocumentListeners(); Element elem = doc.getDefaultRootElement(); if (elem == null) return; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java index 8fce2f08a66..1c36b408d5a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -50,6 +48,7 @@ import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; +import java.awt.event.ActionEvent; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; import java.awt.event.FocusEvent; @@ -62,7 +61,11 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Hashtable; +import javax.swing.AbstractAction; import javax.swing.AbstractButton; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; @@ -77,6 +80,7 @@ import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ToolBarUI; import javax.swing.plaf.UIResource; @@ -87,6 +91,35 @@ import javax.swing.plaf.basic.BasicBorders.ButtonBorder; */ public class BasicToolBarUI extends ToolBarUI implements SwingConstants { + + /** + * Implements the keyboard actions for JToolBar. + */ + static class ToolBarAction + extends AbstractAction + { + /** + * Performs the action. + */ + public void actionPerformed(ActionEvent event) + { + Object cmd = getValue("__command__"); + JToolBar toolBar = (JToolBar) event.getSource(); + BasicToolBarUI ui = (BasicToolBarUI) toolBar.getUI(); + + if (cmd.equals("navigateRight")) + ui.navigateFocusedComp(EAST); + else if (cmd.equals("navigateLeft")) + ui.navigateFocusedComp(WEST); + else if (cmd.equals("navigateUp")) + ui.navigateFocusedComp(NORTH); + else if (cmd.equals("navigateDown")) + ui.navigateFocusedComp(SOUTH); + else + assert false : "Shouldn't reach here"; + } + } + /** Static owner of all DragWindows. * This is package-private to avoid an accessor method. */ static JFrame owner = new JFrame(); @@ -619,9 +652,46 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants * by the look and feel. */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: implement. + // Install the input map. + InputMap inputMap = + (InputMap) SharedUIDefaults.get("ToolBar.ancestorInputMap"); + SwingUtilities.replaceUIInputMap(toolBar, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, + inputMap); + + // FIXME: The JDK uses a LazyActionMap for parentActionMap + SwingUtilities.replaceUIActionMap(toolBar, getActionMap()); + } + + /** + * Fetches the action map from the UI defaults, or create a new one + * if the action map hasn't been initialized. + * + * @return the action map + */ + private ActionMap getActionMap() + { + ActionMap am = (ActionMap) UIManager.get("ToolBar.actionMap"); + if (am == null) + { + am = createDefaultActions(); + UIManager.getLookAndFeelDefaults().put("ToolBar.actionMap", am); + } + return am; + } + + private ActionMap createDefaultActions() + { + ActionMapUIResource am = new ActionMapUIResource(); + Action action = new ToolBarAction(); + + am.put("navigateLeft", action); + am.put("navigateRight", action); + am.put("navigateUp", action); + am.put("navigateDown", action); + + return am; } /** @@ -643,7 +713,12 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants floatFrame.addWindowListener(windowListener); toolBarFocusListener = createToolBarFocusListener(); - toolBar.addFocusListener(toolBarFocusListener); + if (toolBarFocusListener != null) + { + int count = toolBar.getComponentCount(); + for (int i = 0; i < count; i++) + toolBar.getComponent(i).addFocusListener(toolBarFocusListener); + } } /** @@ -758,9 +833,55 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants * @param direction The direction to give focus to. */ protected void navigateFocusedComp(int direction) - throws NotImplementedException { - // FIXME: Implement. + int count = toolBar.getComponentCount(); + switch (direction) + { + case EAST: + case SOUTH: + if (focusedCompIndex >= 0 && focusedCompIndex < count) + { + int i = focusedCompIndex + 1; + boolean focusRequested = false; + // Find component to focus and request focus on it. + while (i != focusedCompIndex && ! focusRequested) + { + if (i >= count) + i = 0; + Component comp = toolBar.getComponentAtIndex(i++); + if (comp != null && comp.isFocusable() + && comp.isEnabled()) + { + comp.requestFocus(); + focusRequested = true; + } + } + } + break; + case WEST: + case NORTH: + if (focusedCompIndex >= 0 && focusedCompIndex < count) + { + int i = focusedCompIndex - 1; + boolean focusRequested = false; + // Find component to focus and request focus on it. + while (i != focusedCompIndex && ! focusRequested) + { + if (i < 0) + i = count - 1; + Component comp = toolBar.getComponentAtIndex(i--); + if (comp != null && comp.isFocusable() + && comp.isEnabled()) + { + comp.requestFocus(); + focusRequested = true; + } + } + } + break; + default: + break; + } } /** @@ -925,9 +1046,10 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants * This method uninstalls keyboard actions installed by the UI. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: implement. + SwingUtilities.replaceUIInputMap(toolBar, JComponent. + WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); + SwingUtilities.replaceUIActionMap(toolBar, null); } /** @@ -935,8 +1057,13 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ protected void uninstallListeners() { - toolBar.removeFocusListener(toolBarFocusListener); - toolBarFocusListener = null; + if (toolBarFocusListener != null) + { + int count = toolBar.getComponentCount(); + for (int i = 0; i < count; i++) + toolBar.getComponent(i).removeFocusListener(toolBarFocusListener); + toolBarFocusListener = null; + } floatFrame.removeWindowListener(windowListener); windowListener = null; @@ -998,7 +1125,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public void mouseClicked(MouseEvent e) { - // Don't care. + // Nothing to do here. } /** @@ -1020,7 +1147,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public void mouseEntered(MouseEvent e) { - // Don't care (yet). + // Nothing to do here. } /** @@ -1030,7 +1157,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public void mouseExited(MouseEvent e) { - // Don't care (yet). + // Nothing to do here. } /** @@ -1040,7 +1167,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public void mouseMoved(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -1203,13 +1330,17 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants } /** - * FIXME: Do something. + * Sets the orientation of the toolbar and the + * drag window. * - * @param o DOCUMENT ME! + * @param o - the new orientation of the toolbar and drag + * window. */ public void setOrientation(int o) { - // FIXME: implement. + toolBar.setOrientation(o); + if (dragWindow != null) + dragWindow.setOrientation(o); } } @@ -1290,6 +1421,10 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants cachedBounds = toolBar.getPreferredSize(); cachedOrientation = toolBar.getOrientation(); + + Component c = e.getChild(); + if (toolBarFocusListener != null) + c.addFocusListener(toolBarFocusListener); } /** @@ -1303,6 +1438,10 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants setBorderToNormal(e.getChild()); cachedBounds = toolBar.getPreferredSize(); cachedOrientation = toolBar.getOrientation(); + + Component c = e.getChild(); + if (toolBarFocusListener != null) + c.removeFocusListener(toolBarFocusListener); } } @@ -1332,27 +1471,30 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ protected ToolBarFocusListener() { - // FIXME: implement. + // Nothing to do here. } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Receives notification when the toolbar or one of it's component + * receives the keyboard input focus. + * + * @param e the focus event */ public void focusGained(FocusEvent e) { - // FIXME: implement. + Component c = e.getComponent(); + focusedCompIndex = toolBar.getComponentIndex(c); } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Receives notification when the toolbar or one of it's component + * looses the keyboard input focus. + * + * @param e the focus event */ public void focusLost(FocusEvent e) { - // FIXME: implement. + // Do nothing here. } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java index 4c139fe465b..9a193986ac5 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java @@ -174,6 +174,9 @@ public class BasicTreeUI /** * Set to false when editing and shouldSelectCall() returns true meaning the * node should be selected before editing, used in completeEditing. + * GNU Classpath editing is implemented differently, so this value is not + * actually read anywhere. However it is always set correctly to maintain + * interoperability with the derived classes that read this field. */ protected boolean stopEditingInCompleteEditing; @@ -235,9 +238,6 @@ public class BasicTreeUI /** Set to true if the editor has a different size than the renderer. */ protected boolean editorHasDifferentSize; - /** The action bound to KeyStrokes. */ - TreeAction action; - /** Boolean to keep track of editing. */ boolean isEditing; @@ -474,8 +474,18 @@ public class BasicTreeUI */ protected void setCellRenderer(TreeCellRenderer tcr) { - currentCellRenderer = tcr; + // Finish editing before changing the renderer. + completeEditing(); + + // The renderer is set in updateRenderer. updateRenderer(); + + // Refresh the layout if necessary. + if (treeState != null) + { + treeState.invalidateSizes(); + updateSize(); + } } /** @@ -845,9 +855,9 @@ public class BasicTreeUI updateRenderer(); updateDepthOffset(); setSelectionModel(tree.getSelectionModel()); - treeState = createLayoutCache(); - treeSelectionModel.setRowMapper(treeState); configureLayoutCache(); + treeState.setRootVisible(tree.isRootVisible()); + treeSelectionModel.setRowMapper(treeState); updateSize(); } @@ -1068,7 +1078,6 @@ public class BasicTreeUI */ protected void uninstallKeyboardActions() { - action = null; tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent( null); tree.getActionMap().setParent(null); @@ -1169,10 +1178,26 @@ public class BasicTreeUI protected void updateRenderer() { if (tree != null) - currentCellRenderer = tree.getCellRenderer(); - - if (currentCellRenderer == null) - currentCellRenderer = createDefaultCellRenderer(); + { + TreeCellRenderer rend = tree.getCellRenderer(); + if (rend != null) + { + createdRenderer = false; + currentCellRenderer = rend; + if (createdCellEditor) + tree.setCellEditor(null); + } + else + { + tree.setCellRenderer(createDefaultCellRenderer()); + createdRenderer = true; + } + } + else + { + currentCellRenderer = null; + createdRenderer = false; + } updateCellEditor(); } @@ -1237,6 +1262,11 @@ public class BasicTreeUI { LookAndFeel.installColorsAndFont(tree, "Tree.background", "Tree.foreground", "Tree.font"); + + hashColor = UIManager.getColor("Tree.hash"); + if (hashColor == null) + hashColor = Color.black; + tree.setOpaque(true); rightChildIndent = UIManager.getInt("Tree.rightChildIndent"); @@ -1264,8 +1294,6 @@ public class BasicTreeUI JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, ancestorInputMap); - action = new TreeAction(); - SwingUtilities.replaceUIActionMap(tree, getActionMap()); } @@ -1295,9 +1323,6 @@ public class BasicTreeUI ActionMapUIResource am = new ActionMapUIResource(); Action action; - action = new TreeAction(); - am.put(action.getValue(Action.NAME), action); - // TreeHomeAction. action = new TreeHomeAction(-1, "selectFirst"); am.put(action.getValue(Action.NAME), action); @@ -1349,6 +1374,13 @@ public class BasicTreeUI am.put(action.getValue(Action.NAME), action); action = new TreePageAction(1, "scrollDownChangeLead"); am.put(action.getValue(Action.NAME), action); + + // Tree editing actions + action = new TreeStartEditingAction("startEditing"); + am.put(action.getValue(Action.NAME), action); + action = new TreeCancelEditingAction("cancel"); + am.put(action.getValue(Action.NAME), action); + return am; } @@ -1703,9 +1735,10 @@ public class BasicTreeUI protected void completeEditing(boolean messageStop, boolean messageCancel, boolean messageTree) { - if (! stopEditingInCompleteEditing || editingComponent == null) + // Make no attempt to complete the non existing editing session. + if (!isEditing(tree)) return; - + if (messageStop) { getCellEditor().stopCellEditing(); @@ -1812,14 +1845,25 @@ public class BasicTreeUI boolean cntlClick = false; if (! treeModel.isLeaf(path.getLastPathComponent())) { - int width = 8; // Only guessing. + int width; Icon expandedIcon = getExpandedIcon(); if (expandedIcon != null) width = expandedIcon.getIconWidth(); + else + // Only guessing. This is the width of + // the tree control icon in Metal L&F. + width = 18; Insets i = tree.getInsets(); - int left = getRowX(tree.getRowForPath(path), path.getPathCount() - 1) - - getRightChildIndent() - width / 2 + i.left; + + int depth; + if (isRootVisible()) + depth = path.getPathCount()-1; + else + depth = path.getPathCount()-2; + + int left = getRowX(tree.getRowForPath(path), depth) + - width + i.left; cntlClick = mouseX >= left && mouseX <= left + width; } return cntlClick; @@ -1848,7 +1892,8 @@ public class BasicTreeUI */ protected void toggleExpandState(TreePath path) { - if (tree.isExpanded(path)) + // tree.isExpanded(path) would do the same, but treeState knows faster. + if (treeState.isExpanded(path)) tree.collapsePath(path); else tree.expandPath(path); @@ -1975,94 +2020,35 @@ public class BasicTreeUI Object node = pathForRow.getLastPathComponent(); return treeModel.isLeaf(node); } - + /** - * This class implements the actions that we want to happen when specific keys - * are pressed for the JTree. The actionPerformed method is called when a key - * that has been registered for the JTree is received. + * The action to start editing at the current lead selection path. */ - class TreeAction + class TreeStartEditingAction extends AbstractAction { - /** - * What to do when this action is called. + * Creates the new tree cancel editing action. + * + * @param name the name of the action (used in toString). + */ + public TreeStartEditingAction(String name) + { + super(name); + } + + /** + * Start editing at the current lead selection path. * * @param e the ActionEvent that caused this action. */ public void actionPerformed(ActionEvent e) { - String command = e.getActionCommand(); TreePath lead = tree.getLeadSelectionPath(); - - if (command.equals("selectPreviousChangeLead") - || command.equals("selectPreviousExtendSelection") - || command.equals("selectPrevious") || command.equals("selectNext") - || command.equals("selectNextExtendSelection") - || command.equals("selectNextChangeLead")) - (new TreeIncrementAction(0, "")).actionPerformed(e); - else if (command.equals("selectParent") || command.equals("selectChild")) - (new TreeTraverseAction(0, "")).actionPerformed(e); - else if (command.equals("selectAll")) - { - TreePath[] paths = new TreePath[treeState.getRowCount()]; - for (int i = 0; i < paths.length; i++) - paths[i] = treeState.getPathForRow(i); - tree.addSelectionPaths(paths); - } - else if (command.equals("startEditing")) + if (!tree.isEditing()) tree.startEditingAtPath(lead); - else if (command.equals("toggle")) - { - if (tree.isEditing()) - tree.stopEditing(); - else - { - Object last = lead.getLastPathComponent(); - TreePath path = new TreePath(getPathToRoot(last, 0)); - if (! treeModel.isLeaf(last)) - toggleExpandState(path); - } - } - else if (command.equals("clearSelection")) - tree.clearSelection(); - - if (tree.isEditing() && ! command.equals("startEditing")) - tree.stopEditing(); - - tree.scrollPathToVisible(tree.getLeadSelectionPath()); } - } - - /** - * This class is used to mimic the behaviour of the JDK when registering - * keyboard actions. It is the same as the private class used in JComponent - * for the same reason. This class receives an action event and dispatches it - * to the true receiver after altering the actionCommand property of the - * event. - */ - private static class ActionListenerProxy - extends AbstractAction - { - ActionListener target; - - String bindingCommandName; - - public ActionListenerProxy(ActionListener li, String cmd) - { - target = li; - bindingCommandName = cmd; - } - - public void actionPerformed(ActionEvent e) - { - ActionEvent derivedEvent = new ActionEvent(e.getSource(), e.getID(), - bindingCommandName, - e.getModifiers()); - - target.actionPerformed(derivedEvent); - } - } + } /** * Updates the preferred size when scrolling, if necessary. @@ -2290,9 +2276,49 @@ public class BasicTreeUI * @param e the key typed */ public void keyTyped(KeyEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + char typed = Character.toLowerCase(e.getKeyChar()); + for (int row = tree.getLeadSelectionRow() + 1; + row < tree.getRowCount(); row++) + { + if (checkMatch(row, typed)) + { + tree.setSelectionRow(row); + tree.scrollRowToVisible(row); + return; + } + } + + // Not found below, search above: + for (int row = 0; row < tree.getLeadSelectionRow(); row++) + { + if (checkMatch(row, typed)) + { + tree.setSelectionRow(row); + tree.scrollRowToVisible(row); + return; + } + } + } + + /** + * Check if the given tree row starts with this character + * + * @param row the tree row + * @param typed the typed char, must be converted to lowercase + * @return true if the given tree row starts with this character + */ + boolean checkMatch(int row, char typed) + { + TreePath path = treeState.getPathForRow(row); + String node = path.getLastPathComponent().toString(); + if (node.length() > 0) + { + char x = node.charAt(0); + if (typed == Character.toLowerCase(x)) + return true; + } + return false; } /** @@ -2301,9 +2327,8 @@ public class BasicTreeUI * @param e the key pressed */ public void keyPressed(KeyEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -2312,9 +2337,8 @@ public class BasicTreeUI * @param e the key released */ public void keyReleased(KeyEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + // Nothing to do here. } } @@ -2341,14 +2365,23 @@ public class BasicTreeUI */ public void mousePressed(MouseEvent e) { + // Any mouse click cancels the previous waiting edit action, initiated + // by the single click on the selected node. + if (startEditTimer != null) + { + startEditTimer.stop(); + startEditTimer = null; + } if (tree != null && tree.isEnabled()) { - // Maybe stop editing and return. - if (isEditing(tree) && tree.getInvokesStopCellEditing() - && !stopEditing(tree)) - return; - + // Always end the current editing session if clicked on the + // tree and outside the bounds of the editing component. + if (isEditing(tree)) + if (!stopEditing(tree)) + // Return if we have failed to cancel the editing session. + return; + int x = e.getX(); int y = e.getY(); TreePath path = getClosestPathForLocation(tree, x, y); @@ -2361,11 +2394,47 @@ public class BasicTreeUI if (x > bounds.x && x <= (bounds.x + bounds.width)) { - if (! startEditing(path, e)) - selectPathForEvent(path, e); + TreePath currentLead = tree.getLeadSelectionPath(); + if (currentLead != null && currentLead.equals(path) + && e.getClickCount() == 1 && tree.isEditable()) + { + // Schedule the editing session. + final TreePath editPath = path; + + // The code below handles the required click-pause-click + // functionality which must be present in the tree UI. + // If the next click comes after the + // time longer than the double click interval AND + // the same node stays focused for the WAIT_TILL_EDITING + // duration, the timer starts the editing session. + if (startEditTimer != null) + startEditTimer.stop(); + + startEditTimer = new Timer(WAIT_TILL_EDITING, + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + startEditing(editPath, EDIT); + } + }); + + startEditTimer.setRepeats(false); + startEditTimer.start(); + } + else + { + if (e.getClickCount() == 2) + toggleExpandState(path); + else + selectPathForEvent(path, e); + } } } } + + // We need to request the focus. + tree.requestFocusInWindow(); } /** @@ -2830,6 +2899,9 @@ public class BasicTreeUI } } } + + // Ensure that the lead path is visible after the increment action. + tree.scrollPathToVisible(tree.getLeadSelectionPath()); } /** @@ -2945,6 +3017,9 @@ public class BasicTreeUI tree.setAnchorSelectionPath(newPath); tree.setLeadSelectionPath(newPath); } + + // Ensure that the lead path is visible after the increment action. + tree.scrollPathToVisible(tree.getLeadSelectionPath()); } /** @@ -3249,6 +3324,9 @@ public class BasicTreeUI // and anchor. tree.setLeadSelectionPath(leadPath); tree.setAnchorSelectionPath(anchorPath); + + // Ensure that the lead path is visible after the increment action. + tree.scrollPathToVisible(tree.getLeadSelectionPath()); } } @@ -3336,6 +3414,9 @@ public class BasicTreeUI tree.expandPath(current); } } + + // Ensure that the lead path is visible after the increment action. + tree.scrollPathToVisible(tree.getLeadSelectionPath()); } /** @@ -3644,7 +3725,13 @@ public class BasicTreeUI { Rectangle bounds = getPathBounds(tree, path); TreePath parent = path.getParentPath(); - if (parent != null) + + boolean paintLine; + if (isRootVisible()) + paintLine = parent != null; + else + paintLine = parent != null && parent.getPathCount() > 1; + if (paintLine) { Rectangle parentBounds = getPathBounds(tree, parent); paintVerticalLine(g, tree, parentBounds.x + 2 * gap, diff --git a/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java b/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java index 47876491160..34278052bc1 100644 --- a/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java +++ b/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.util.HashMap; import javax.swing.UIManager; @@ -75,4 +76,16 @@ public class SharedUIDefaults } return o; } + + /** + * Returns a shared UI color. + * + * @param key the key + * + * @return the shared color instance + */ + static Color getColor(String key) + { + return (Color) get(key); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java index 6a528de2b6b..265ea7ef6ae 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java @@ -259,7 +259,9 @@ public class MetalComboBoxButton Component comp = renderer.getListCellRendererComponent(listBox, comboBox.getSelectedItem(), -1, false, false); comp.setFont(rendererPane.getFont()); - if (model.isArmed() && model.isPressed()) + + if ((model.isArmed() && model.isPressed()) + || (comboBox.isFocusOwner() && !comboBox.isPopupVisible())) { if (isOpaque()) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java index 1219ad9fd11..824f1d8021b 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -42,6 +42,7 @@ import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Insets; import java.awt.LayoutManager; @@ -303,8 +304,9 @@ public class MetalFileChooserUI if (file == null) setFileName(null); - else - setFileName(file.getName()); + else if (file.isFile() || filechooser.getFileSelectionMode() + != JFileChooser.FILES_ONLY) + setFileName(file.getName()); int index = -1; index = getModel().indexOf(file); if (index >= 0) @@ -567,10 +569,17 @@ public class MetalFileChooserUI extends DefaultListCellRenderer { /** + * This is the icon that is displayed in the combobox. This wraps + * the standard icon and adds indendation. + */ + private IndentIcon indentIcon; + + /** * Creates a new renderer. */ public DirectoryComboBoxRenderer(JFileChooser fc) - { + { + indentIcon = new IndentIcon(); } /** @@ -586,31 +595,86 @@ public class MetalFileChooserUI * @return The list cell renderer. */ public Component getListCellRendererComponent(JList list, Object value, - int index, boolean isSelected, boolean cellHasFocus) + int index, + boolean isSelected, + boolean cellHasFocus) { - FileView fileView = getFileView(getFileChooser()); + super.getListCellRendererComponent(list, value, index, isSelected, + cellHasFocus); File file = (File) value; - setIcon(fileView.getIcon(file)); - setText(fileView.getName(file)); - - if (isSelected) - { - setBackground(list.getSelectionBackground()); - setForeground(list.getSelectionForeground()); - } - else - { - setBackground(list.getBackground()); - setForeground(list.getForeground()); - } + setText(getFileChooser().getName(file)); + + // Install indented icon. + Icon icon = getFileChooser().getIcon(file); + indentIcon.setIcon(icon); + int depth = directoryModel.getDepth(index); + indentIcon.setDepth(depth); + setIcon(indentIcon); - setEnabled(list.isEnabled()); - setFont(list.getFont()); return this; } } /** + * An icon that wraps another icon and adds indentation. + */ + class IndentIcon + implements Icon + { + + /** + * The indentation level. + */ + private static final int INDENT = 10; + + /** + * The wrapped icon. + */ + private Icon icon; + + /** + * The current depth. + */ + private int depth; + + /** + * Sets the icon to be wrapped. + * + * @param i the icon + */ + void setIcon(Icon i) + { + icon = i; + } + + /** + * Sets the indentation depth. + * + * @param d the depth to set + */ + void setDepth(int d) + { + depth = d; + } + + public int getIconHeight() + { + return icon.getIconHeight(); + } + + public int getIconWidth() + { + return icon.getIconWidth() + depth * INDENT; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + icon.paintIcon(c, g, x + depth * INDENT, y); + } + + } + + /** * A renderer for the files and directories in the file chooser. */ protected class FileRenderer @@ -956,9 +1020,12 @@ public class MetalFileChooserUI { String text = editField.getText(); if (text != null && text != "" && !text.equals(fc.getName(editFile))) - if (editFile.renameTo(fc.getFileSystemView().createFileObject( - fc.getCurrentDirectory(), text))) + { + File f = fc.getFileSystemView(). + createFileObject(fc.getCurrentDirectory(), text); + if ( editFile.renameTo(f) ) rescanCurrentDirectory(fc); + } list.remove(editField); } startEditing = false; @@ -982,17 +1049,8 @@ public class MetalFileChooserUI */ public void actionPerformed(ActionEvent e) { - if (e.getActionCommand().equals("notify-field-accept")) + if (editField != null) completeEditing(); - else if (editField != null) - { - list.remove(editField); - startEditing = false; - editFile = null; - lastSelected = null; - editField = null; - list.repaint(); - } } } } @@ -1101,7 +1159,7 @@ public class MetalFileChooserUI lastSelected = selVal; if (f.isFile()) setFileName(path.substring(path.lastIndexOf("/") + 1)); - else if (fc.getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) + else if (fc.getFileSelectionMode() != JFileChooser.FILES_ONLY) setFileName(path); } fileTable.repaint(); @@ -1171,16 +1229,8 @@ public class MetalFileChooserUI */ public void actionPerformed(ActionEvent e) { - if (e.getActionCommand().equals("notify-field-accept")) + if (editField != null) completeEditing(); - else if (editField != null) - { - table.remove(editField); - startEditing = false; - editFile = null; - editField = null; - table.repaint(); - } } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java index a317e3fc00d..30ec7e72b28 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java @@ -1,5 +1,5 @@ /* MetalIconFactory.java -- - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -54,6 +54,7 @@ import javax.swing.JRadioButtonMenuItem; import javax.swing.JSlider; import javax.swing.SwingConstants; import javax.swing.UIManager; +import javax.swing.plaf.IconUIResource; import javax.swing.plaf.UIResource; @@ -78,7 +79,8 @@ public class MetalIconFactory implements Serializable /** * An icon displayed for {@link JCheckBoxMenuItem} components. */ - private static class CheckBoxMenuItemIcon implements Icon, Serializable + private static class CheckBoxMenuItemIcon + implements Icon, UIResource, Serializable { /** * Creates a new icon instance. @@ -153,7 +155,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getFileChooserDetailViewIcon() */ - private static class FileChooserDetailViewIcon implements Icon, Serializable + private static class FileChooserDetailViewIcon + implements Icon, UIResource, Serializable { /** @@ -233,7 +236,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getFileChooserHomeFolderIcon() */ - private static class FileChooserHomeFolderIcon implements Icon, Serializable + private static class FileChooserHomeFolderIcon + implements Icon, UIResource, Serializable { /** @@ -322,7 +326,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getFileChooserListViewIcon() */ - private static class FileChooserListViewIcon implements Icon, Serializable + private static class FileChooserListViewIcon + implements Icon, UIResource, Serializable { /** * Creates a new icon. @@ -418,7 +423,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getFileChooserNewFolderIcon() */ - private static class FileChooserNewFolderIcon implements Icon, Serializable + private static class FileChooserNewFolderIcon + implements Icon, UIResource, Serializable { /** * Creates a new icon. @@ -490,8 +496,7 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getFileChooserNewFolderIcon() */ - private static class FileChooserUpFolderIcon extends FileChooserNewFolderIcon - implements Icon, Serializable + private static class FileChooserUpFolderIcon extends FileChooserNewFolderIcon { /** * Creates a new icon. @@ -883,7 +888,8 @@ public class MetalIconFactory implements Serializable /** * An icon displayed for {@link JRadioButtonMenuItem} components. */ - private static class RadioButtonMenuItemIcon implements Icon, Serializable + private static class RadioButtonMenuItemIcon + implements Icon, UIResource, Serializable { /** * Creates a new icon instance. @@ -960,7 +966,8 @@ public class MetalIconFactory implements Serializable * The icon used to display the thumb control on a horizontally oriented * {@link JSlider} component. */ - private static class HorizontalSliderThumbIcon implements Icon, Serializable + private static class HorizontalSliderThumbIcon + implements Icon, UIResource, Serializable { /** @@ -1102,7 +1109,8 @@ public class MetalIconFactory implements Serializable * An icon used for the 'close' button in the title frame of a * {@link JInternalFrame}. */ - private static class InternalFrameCloseIcon implements Icon, Serializable + private static class InternalFrameCloseIcon + implements Icon, UIResource, Serializable { /** The icon size in pixels. */ private int size; @@ -1219,7 +1227,7 @@ public class MetalIconFactory implements Serializable * The icon displayed at the top-left corner of a {@link JInternalFrame}. */ private static class InternalFrameDefaultMenuIcon - implements Icon, Serializable + implements Icon, UIResource, Serializable { /** @@ -1291,7 +1299,7 @@ public class MetalIconFactory implements Serializable * provide a 'restore' option. */ private static class InternalFrameAltMaximizeIcon - implements Icon, Serializable + implements Icon, UIResource, Serializable { /** The icon size in pixels. */ private int size; @@ -1401,7 +1409,8 @@ public class MetalIconFactory implements Serializable * An icon used for the 'maximize' button in the title frame of a * {@link JInternalFrame}. */ - private static class InternalFrameMaximizeIcon implements Icon, Serializable + private static class InternalFrameMaximizeIcon + implements Icon, UIResource, Serializable { /** @@ -1513,7 +1522,8 @@ public class MetalIconFactory implements Serializable /** * An icon used in the title frame of a {@link JInternalFrame}. */ - private static class InternalFrameMinimizeIcon implements Icon, Serializable + private static class InternalFrameMinimizeIcon + implements Icon, UIResource, Serializable { /** @@ -1617,7 +1627,8 @@ public class MetalIconFactory implements Serializable * The icon used to display the thumb control on a horizontally oriented * {@link JSlider} component. */ - private static class VerticalSliderThumbIcon implements Icon, Serializable + private static class VerticalSliderThumbIcon + implements Icon, UIResource, Serializable { /** * This mask is used to paint the gradient in the shape of the thumb. @@ -1801,78 +1812,36 @@ public class MetalIconFactory implements Serializable /** * Paints the icon at the location (x, y). * - * @param c the component. - * @param g the graphics device. - * @param x the x coordinate. - * @param y the y coordinate. + * @param c the component. + * @param g the graphics device. + * @param x the x coordinate. + * @param y the y coordinate. */ - public void paintIcon(Component c, Graphics g, int x, int y) + public void paintIcon(Component c, Graphics g, int x, int y) { - x = x + 5; - y = y + 5; - if (collapsed) - { - // TODO: pick up appropriate UI colors - g.setColor(Color.black); - g.drawLine(x + 2, y, x + 5, y); - g.drawLine(x + 6, y + 1, x + 7, y + 2); - g.fillRect(x + 7, y + 3, 5, 2); - g.drawLine(x + 7, y + 5, x + 6, y + 6); - g.drawLine(x + 1, y + 1, x + 1, y + 1); - g.drawLine(x, y + 2, x, y + 5); - g.drawLine(x + 1, y + 6, x + 1, y + 6); - g.drawLine(x + 2, y + 7, x + 5, y + 7); - g.fillRect(x + 3, y + 3, 2, 2); - - g.setColor(new Color(204, 204, 255)); - g.drawLine(x + 3, y + 2, x + 4, y + 2); - g.drawLine(x + 2, y + 3, x + 2, y + 4); - g.drawLine(x + 3, y + 5, x + 3, y + 5); - g.drawLine(x + 5, y + 3, x + 5, y + 3); - - g.setColor(new Color(153, 153, 204)); - g.drawLine(x + 2, y + 2, x + 2, y + 2); - g.drawLine(x + 2, y + 5, x + 2, y + 5); - g.drawLine(x + 2, y + 6, x + 5, y + 6); - g.drawLine(x + 5, y + 2, x + 5, y + 2); - g.drawLine(x + 6, y + 2, x + 6, y + 5); - - g.setColor(new Color(102, 102, 153)); - g.drawLine(x + 2, y + 1, x + 5, y + 1); - g.drawLine(x + 1, y + 2, x + 1, y + 5); - } + // TODO: pick up appropriate UI colors + Color dark = new Color(99, 130, 191); + Color light = new Color(163, 184, 204); + Color white = Color.white; + + x += 8; + y += 6; + + final int w = 6; + final int wHalf = (w >> 2); + g.setColor(light); + g.drawOval(x, y, w, w); + g.setColor(dark); + g.fillOval(x + 1, y + 1, w - 1, w - 1); + + if (collapsed) + g.fillRect(x + w, y + wHalf + 1, w, 2); else - { - // TODO: pick up appropriate UI colors - g.setColor(Color.black); - g.drawLine(x + 2, y, x + 5, y); - g.drawLine(x + 6, y + 1, x + 7, y + 2); - g.drawLine(x + 7, y + 2, x + 7, y + 5); - g.fillRect(x + 3, y + 7, 2, 5); - g.drawLine(x + 7, y + 5, x + 6, y + 6); - g.drawLine(x + 1, y + 1, x + 1, y + 1); - g.drawLine(x, y + 2, x, y + 5); - g.drawLine(x + 1, y + 6, x + 1, y + 6); - g.drawLine(x + 2, y + 7, x + 5, y + 7); - g.fillRect(x + 3, y + 3, 2, 2); - - g.setColor(new Color(204, 204, 255)); - g.drawLine(x + 3, y + 2, x + 4, y + 2); - g.drawLine(x + 2, y + 3, x + 2, y + 4); - g.drawLine(x + 3, y + 5, x + 3, y + 5); - g.drawLine(x + 5, y + 3, x + 5, y + 3); - - g.setColor(new Color(153, 153, 204)); - g.drawLine(x + 2, y + 2, x + 2, y + 2); - g.drawLine(x + 2, y + 5, x + 2, y + 5); - g.drawLine(x + 2, y + 6, x + 5, y + 6); - g.drawLine(x + 5, y + 2, x + 5, y + 2); - g.drawLine(x + 6, y + 2, x + 6, y + 5); - - g.setColor(new Color(102, 102, 153)); - g.drawLine(x + 2, y + 1, x + 5, y + 1); - g.drawLine(x + 1, y + 2, x + 1, y + 5); - } + g.fillRect(x + wHalf + 1, y + w, 2, w); + + g.setColor(white); + g.fillRect(x + wHalf + 1, y + wHalf + 1, 2, 2); + } /** @@ -1964,7 +1933,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getTreeHardDriveIcon() */ - private static class TreeHardDriveIcon implements Icon, Serializable + private static class TreeHardDriveIcon + implements Icon, UIResource, Serializable { /** @@ -2074,7 +2044,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getTreeFloppyDriveIcon() */ - private static class TreeFloppyDriveIcon implements Icon, Serializable + private static class TreeFloppyDriveIcon + implements Icon, UIResource, Serializable { /** @@ -2150,7 +2121,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getTreeComputerIcon() */ - private static class TreeComputerIcon implements Icon, Serializable + private static class TreeComputerIcon + implements Icon, UIResource, Serializable { /** @@ -2255,6 +2227,12 @@ public class MetalIconFactory implements Serializable /** The icon instance returned by {@link #getTreeHardDriveIcon()}. */ private static Icon treeHardDriveIcon; + /** The icon instance returned by {@link #getHorizontalSliderThumbIcon()}. */ + private static Icon horizontalSliderThumbIcon; + + /** The icon instance returned by {@link #getVerticalSliderThumbIcon()}. */ + private static Icon verticalSliderThumbIcon; + /** * Creates a new instance. All the methods are static, so creating an * instance isn't necessary. @@ -2383,7 +2361,9 @@ public class MetalIconFactory implements Serializable */ public static Icon getHorizontalSliderThumbIcon() { - return new HorizontalSliderThumbIcon(); + if (horizontalSliderThumbIcon == null) + horizontalSliderThumbIcon = new HorizontalSliderThumbIcon(); + return horizontalSliderThumbIcon; } /** @@ -2462,7 +2442,9 @@ public class MetalIconFactory implements Serializable */ public static Icon getVerticalSliderThumbIcon() { - return new VerticalSliderThumbIcon(); + if (verticalSliderThumbIcon == null) + verticalSliderThumbIcon = new VerticalSliderThumbIcon(); + return verticalSliderThumbIcon; } /** diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java index 09eafd40fe9..8a5a61107c1 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -85,7 +85,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel } /** - * Sets the current theme to a new instance of {@link DefaultMetalTheme}. + * Sets the current theme to a new instance of {@link OceanTheme}. */ protected void createDefaultTheme() { @@ -709,6 +709,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel * @param theme the theme (<code>null</code> not permitted). * * @throws NullPointerException if <code>theme</code> is <code>null</code>. + * + * @see #getCurrentTheme() */ public static void setCurrentTheme(MetalTheme theme) { @@ -1183,20 +1185,26 @@ public class MetalLookAndFeel extends BasicLookAndFeel "SplitPaneDivider.draggingColor", Color.DARK_GRAY, "TabbedPane.background", getControlShadow(), + "TabbedPane.contentBorderInsets", new InsetsUIResource(2, 2, 3, 3), + "TabbedPane.contentOpaque", Boolean.TRUE, "TabbedPane.darkShadow", getControlDarkShadow(), "TabbedPane.focus", getPrimaryControlDarkShadow(), "TabbedPane.font", new FontUIResource("Dialog", Font.BOLD, 12), "TabbedPane.foreground", getControlTextColor(), "TabbedPane.highlight", getControlHighlight(), "TabbedPane.light", getControl(), - "TabbedPane.selected", getControl(), + "TabbedPane.selected", getControl(), // overridden in OceanTheme "TabbedPane.selectHighlight", getControlHighlight(), "TabbedPane.selectedTabPadInsets", new InsetsUIResource(2, 2, 2, 1), "TabbedPane.shadow", getControlShadow(), - "TabbedPane.tabAreaBackground", getControl(), - "TabbedPane.tabAreaInsets", new InsetsUIResource(4, 2, 0, 6), + "TabbedPane.tabAreaBackground", getControl(), // overridden in OceanTheme + "TabbedPane.tabAreaInsets", new InsetsUIResource(4, 2, 0, 6), // dito "TabbedPane.tabInsets", new InsetsUIResource(0, 9, 1, 9), + // new properties in OceanTheme: + // TabbedPane.contentAreaColor + // TabbedPane.unselectedBackground + "Table.background", getWindowBackground(), "Table.focusCellBackground", getWindowBackground(), "Table.focusCellForeground", getControlTextColor(), @@ -1243,6 +1251,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "TextPane.selectionBackground", getTextHighlightColor(), "TextPane.selectionForeground", getHighlightedTextColor(), + "TitledBorder.border", new LineBorderUIResource(getPrimaryControl(), 1), "TitledBorder.font", new FontUIResource("Dialog", Font.BOLD, 12), "TitledBorder.titleColor", getSystemTextColor(), @@ -1335,12 +1344,17 @@ public class MetalLookAndFeel extends BasicLookAndFeel } /** - * Returns the current theme setting for the Metal L&F. + * Returns the current theme for the Metal look and feel. The default is + * an instance of {@link OceanTheme}. * - * @return the current theme setting for the Metal L&F + * @return The current theme (never <code>null</code>). + * + * @see #setCurrentTheme(MetalTheme) */ public static MetalTheme getCurrentTheme() { + if (theme == null) + theme = new OceanTheme(); return theme; } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java index 31d8d671fa1..ff763ea9da9 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java @@ -44,6 +44,7 @@ import javax.swing.JComponent; import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicMenuBarUI; /** @@ -75,7 +76,9 @@ public class MetalMenuBarUI extends BasicMenuBarUI */ public void update(Graphics g, JComponent c) { - if (c.isOpaque() && UIManager.get("MenuBar.gradient") != null) + if (c.isOpaque() + && UIManager.get("MenuBar.gradient") != null + && c.getBackground() instanceof UIResource) { MetalUtils.paintGradient(g, 0, 0, c.getWidth(), c.getHeight(), SwingConstants.VERTICAL, "MenuBar.gradient"); diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java index 84f9cfe494e..a55dc091665 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java @@ -84,6 +84,7 @@ public class MetalScrollButton extends BasicArrowButton super(direction); buttonWidth = width; this.freeStanding = freeStanding; + setFocusable(false); } /** diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java index c49abe832e4..20135fc857e 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java @@ -1,5 +1,5 @@ /* MetalTabbedPaneUI.java - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -170,7 +170,9 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI */ protected LayoutManager createLayoutManager() { - return new TabbedPaneLayout(); + return (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) + ? new MetalTabbedPaneUI.TabbedPaneLayout() + : super.createLayoutManager(); } /** @@ -326,7 +328,6 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI int bottom = h - 1; int right = w - 1; - int tabCount = tabPane.getTabCount(); int currentRun = getRunForTab(tabCount, tabIndex); int firstIndex = tabRuns[currentRun]; @@ -396,14 +397,17 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI { if (tabPane.getSelectedIndex() == tabIndex - 1) { - g.drawLine(0, 5, 0, bottom); - g.setColor(oceanSelectedBorder); - g.drawLine(0, 0, 0, 5); + g.drawLine(0, 6, 0, bottom); + if (tabIndex != firstIndex) + { + g.setColor(oceanSelectedBorder); + g.drawLine(0, 0, 0, 5); + } } else if (isSelected) { g.drawLine(0, 5, 0, bottom); - if (tabIndex != 0) + if (tabIndex != firstIndex) { g.setColor(darkShadow); g.drawLine(0, 0, 0, 5); @@ -463,9 +467,10 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI { Color c; if (tabPane.getSelectedIndex() == tabIndex - 1) - c = UIManager.getColor("TabbedPane.tabAreaBackground"); + c = selectColor; else c = getUnselectedBackground(tabIndex - 1); + g.setColor(c); g.fillRect(right - 5, 0, 5, 3); g.fillRect(right - 2, 3, 2, 2); } @@ -522,10 +527,13 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI } else if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1) { - g.setColor(oceanSelectedBorder); - g.drawLine(right, 0, right, 6); + if (tabIndex != firstIndex) + { + g.setColor(oceanSelectedBorder); + g.drawLine(right, 0, right, 6); + } g.setColor(darkShadow); - g.drawLine(right, 6, right, bottom); + g.drawLine(right, 7, right, bottom); } else if (tabIndex != firstIndex) { @@ -598,8 +606,10 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI if (isOcean && isSelected) { g.drawLine(0, 0, 0, bottom - 5); - if ((currentRun == 0 && tabIndex != 0) - || (currentRun > 0 && tabIndex != tabRuns[currentRun - 1])) + + // Paint a connecting line to the tab below for all + // but the first tab in the last run. + if (tabIndex != tabRuns[runCount-1]) { g.setColor(darkShadow); g.drawLine(0, bottom - 5, 0, bottom); @@ -688,6 +698,103 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI } /** + * This method paints the focus rectangle around the selected tab. + * + * @param g The Graphics object to paint with. + * @param tabPlacement The JTabbedPane's tab placement. + * @param rects The array of rectangles keeping track of size and position. + * @param tabIndex The tab index. + * @param iconRect The icon bounds. + * @param textRect The text bounds. + * @param isSelected Whether this tab is selected. + */ + protected void paintFocusIndicator(Graphics g, int tabPlacement, + Rectangle[] rects, int tabIndex, + Rectangle iconRect, Rectangle textRect, + boolean isSelected) + { + if (tabPane.hasFocus() && isSelected) + { + Rectangle rect = rects[tabIndex]; + + g.setColor(focus); + g.translate(rect.x, rect.y); + + switch (tabPlacement) + { + case LEFT: + // Top line + g.drawLine(7, 2, rect.width-2, 2); + + // Right line + g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3); + + // Bottom line + g.drawLine(rect.width-2, rect.height-2, 3, rect.height-2); + + // Left line + g.drawLine(2, rect.height-3, 2, 7); + + // Slant + g.drawLine(2, 6, 6, 2); + break; + case RIGHT: + // Top line + g.drawLine(1, 2, rect.width-8, 2); + + // Slant + g.drawLine(rect.width-7, 2, rect.width-3, 6); + + // Right line + g.drawLine(rect.width-3, 7, rect.width-3, rect.height-3); + + // Bottom line + g.drawLine(rect.width-3, rect.height-2, 2, rect.height-2); + + // Left line + g.drawLine(1, rect.height-2, 1, 2); + break; + case BOTTOM: + // Top line + g.drawLine(2, 1, rect.width-2, 1); + + // Right line + g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3); + + // Bottom line + g.drawLine(7, rect.height-3, rect.width-2, rect.height-3); + + // Slant + g.drawLine(6, rect.height-3, 2, rect.height-7); + + // Left line + g.drawLine(2, rect.height-8, 2, 2); + + break; + case TOP: + default: + // Top line + g.drawLine(6, 2, rect.width-2, 2); + + // Right line + g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3); + + // Bottom line + g.drawLine(3, rect.height-3, rect.width-2, rect.height-3); + + // Left line + g.drawLine(2, rect.height-3, 2, 7); + + // Slant + g.drawLine(2, 6, 6, 2); + + } + + g.translate(-rect.x, -rect.y); + } + } + + /** * Returns <code>true</code> if the tabs in the specified run should be * padded to make the run fill the width/height of the {@link JTabbedPane}. * @@ -1144,4 +1251,19 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI bg = unselectedBackground; return bg; } + + protected int getTabLabelShiftX(int tabPlacement, + int index, + boolean isSelected) + { + return 0; + } + + protected int getTabLabelShiftY(int tabPlacement, + int index, + boolean isSelected) + { + return 0; + } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java index 72cbb34a6dc..0c3a38d5cc3 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java @@ -41,6 +41,7 @@ import gnu.classpath.SystemProperties; import java.awt.Color; import java.awt.Component; +import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.TexturePaint; @@ -106,7 +107,7 @@ class MetalUtils for (int mX = x + xOff; mX < (x + w); mX += 4) { - g.drawLine(mX, mY, mX, mY); + g.fillRect(mX, mY, 1, 1); } // increase x offset @@ -305,6 +306,15 @@ class MetalUtils float g1, float g2, Color c1, Color c2, Color c3, int[][] mask) { + + if (g instanceof Graphics2D + && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") == null) + { + paintHorizontalGradient2D((Graphics2D) g, x, y, w, h, g1, g2, c1, c2, + c3, mask); + return; + } + // Calculate the coordinates. int y0 = y; int y1 = y + h; @@ -339,7 +349,7 @@ class MetalUtils y0 = mask[xc - x0][0] + y; y1 = mask[xc - x0][1] + y; } - g.drawLine(xc, y0, xc, y1); + g.fillRect(xc, y0, 1, y1 - y0); } // Paint solid c2 area. g.setColor(c2); @@ -353,7 +363,7 @@ class MetalUtils { y0 = mask[xc - x0][0] + y; y1 = mask[xc - x0][1] + y; - g.drawLine(xc, y0, xc, y1); + g.fillRect(xc, y0, 1, y1 - y0); } } @@ -377,7 +387,7 @@ class MetalUtils y0 = mask[xc - x0][0] + y; y1 = mask[xc - x0][1] + y; } - g.drawLine(xc, y0, xc, y1); + g.fillRect(xc, y0, 1, y1 - y0); } // Paint third gradient area (c1->c3). @@ -421,9 +431,17 @@ class MetalUtils * described above */ static void paintVerticalGradient(Graphics g, int x, int y, int w, int h, - double g1, double g2, Color c1, Color c2, + float g1, float g2, Color c1, Color c2, Color c3, int[][] mask) { + if (g instanceof Graphics2D + && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") == null) + { + paintVerticalGradient2D((Graphics2D) g, x, y, w, h, g1, g2, c1, c2, + c3, mask); + return; + } + // Calculate the coordinates. int x0 = x; int x1 = x + w; @@ -458,7 +476,7 @@ class MetalUtils x0 = mask[yc - y0][0] + x; x1 = mask[yc - y0][1] + x; } - g.drawLine(x0, yc, x1, yc); + g.fillRect(x0, yc, x1 - x0, 1); } // Paint solid c2 area. g.setColor(c2); @@ -472,7 +490,7 @@ class MetalUtils { x0 = mask[yc - y0][0] + x; x1 = mask[yc - y0][1] + x; - g.drawLine(x0, yc, x1, yc); + g.fillRect(x0, yc, x1 - x0, 1); } } @@ -496,7 +514,7 @@ class MetalUtils x0 = mask[yc - y0][0] + x; x1 = mask[yc - y0][1] + x; } - g.drawLine(x0, yc, x1, yc); + g.fillRect(x0, yc, x1 - x0, 1); } // Paint third gradient area (c1->c3). @@ -519,7 +537,61 @@ class MetalUtils x0 = mask[yc - y0][0] + x; x1 = mask[yc - y0][1] + x; } - g.drawLine(x0, yc, x1, yc); + g.fillRect(x0, yc, x1 - x0, 1); } } + + /** + * Paints a horizontal gradient using Graphics2D functionality. + * + * @param g the Graphics2D instance + * @param x the X coordinate of the upper left corner of the rectangle + * @param y the Y coordinate of the upper left corner of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param g1 the relative width of the c1->c2 gradients + * @param g2 the relative width of the c2 solid area + * @param c1 the color 1 + * @param c2 the color 2 + * @param c3 the color 3 + * @param mask the mask that should be used when painting the gradient as + * described above + */ + private static void paintHorizontalGradient2D(Graphics2D g, int x, int y, + int w, int h, float g1, + float g2, Color c1, + Color c2, Color c3, + int[][] mask) + { + // FIXME: Handle the mask somehow, or do Graphics2D clipping instead. + GradientPaint p1 = new GradientPaint(x, y, c1, x + w * g1, y, c2); + g.setPaint(p1); + // This fills the first gradient and the solid area in one go. + g.fillRect(x, y, (int) (w * (g1 + g2)), h); + + GradientPaint p2 = new GradientPaint(x + (w * (g1 + g2)), y, c2, x + w, y, + c3); + g.setPaint(p2); + g.fillRect((int) (x + (w * (g1 + g2))), y, + (int) (w * (1. - (g1 + g2))), h); + } + + private static void paintVerticalGradient2D(Graphics2D g, int x, int y, + int w, int h, float g1, + float g2, Color c1, + Color c2, Color c3, + int[][] mask) + { + // FIXME: Handle the mask somehow, or do Graphics2D clipping instead. + GradientPaint p1 = new GradientPaint(x, y, c1, x, y + h * g1, c2); + g.setPaint(p1); + // This fills the first gradient and the solid area in one go. + g.fillRect(x, y, w, (int) (h * (g1 + g2))); + + GradientPaint p2 = new GradientPaint(x, y + (h * (g1 + g2)), c2, x, y + h, + c3); + g.setPaint(p2); + g.fillRect(x, (int) (y + (h * (g1 + g2))), w, + (int) (h * (1. - (g1 + g2)))); + } } diff --git a/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java b/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java index 24ac8fc9c39..33e68ea9fcd 100644 --- a/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java +++ b/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java @@ -91,10 +91,10 @@ public class DefaultTableColumnModel /** * A change event used when notifying listeners of a change to the * <code>columnMargin</code> field. This single event is reused for all - * notifications. + * notifications (it is lazily instantiated within the + * {@link #fireColumnMarginChanged()} method). */ - // FIXME: use lazy instantiation - protected transient ChangeEvent changeEvent = new ChangeEvent(this); + protected transient ChangeEvent changeEvent; /** * A flag that indicates whether or not columns can be selected. @@ -580,7 +580,9 @@ public class DefaultTableColumnModel */ protected void fireColumnMarginChanged() { - EventListener [] listeners = getListeners(TableColumnModelListener.class); + EventListener[] listeners = getListeners(TableColumnModelListener.class); + if (changeEvent == null && listeners.length > 0) + changeEvent = new ChangeEvent(this); for (int i = 0; i < listeners.length; ++i) ((TableColumnModelListener) listeners[i]).columnMarginChanged(changeEvent); } diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java index 1ef81732fed..eb46a8c42f6 100644 --- a/libjava/classpath/javax/swing/text/AbstractDocument.java +++ b/libjava/classpath/javax/swing/text/AbstractDocument.java @@ -147,14 +147,19 @@ public abstract class AbstractDocument implements Document, Serializable /** * A condition variable that readers and writers wait on. */ - Object documentCV = new Object(); + private Object documentCV = new Object(); /** An instance of a DocumentFilter.FilterBypass which allows calling * the insert, remove and replace method without checking for an installed * document filter. */ - DocumentFilter.FilterBypass bypass; - + private DocumentFilter.FilterBypass bypass; + + /** + * The bidi root element. + */ + private Element bidiRoot; + /** * Creates a new <code>AbstractDocument</code> with the specified * {@link Content} model. @@ -185,6 +190,13 @@ public abstract class AbstractDocument implements Document, Serializable { content = doc; context = ctx; + + // FIXME: This is determined using a Mauve test. Make the document + // actually use this. + putProperty("i18n", Boolean.FALSE); + + // FIXME: Fully implement bidi. + bidiRoot = new BranchElement(null, null); } /** Returns the DocumentFilter.FilterBypass instance for this @@ -360,7 +372,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public Element getBidiRootElement() { - return null; + return bidiRoot; } /** @@ -477,8 +489,9 @@ public abstract class AbstractDocument implements Document, Serializable */ public Element[] getRootElements() { - Element[] elements = new Element[1]; + Element[] elements = new Element[2]; elements[0] = getDefaultRootElement(); + elements[1] = getBidiRootElement(); return elements; } @@ -739,29 +752,36 @@ public abstract class AbstractDocument implements Document, Serializable void removeImpl(int offset, int length) throws BadLocationException { - // Prevent some unneccessary method invocation (observed in the RI). - if (length <= 0) - return; - - DefaultDocumentEvent event = - new DefaultDocumentEvent(offset, length, - DocumentEvent.EventType.REMOVE); - - try + // The RI silently ignores all requests that have a negative length. + // Don't ask my why, but that's how it is. + if (length > 0) { - writeLock(); + if (offset < 0 || offset > getLength()) + throw new BadLocationException("Invalid remove position", offset); + + if (offset + length > getLength()) + throw new BadLocationException("Invalid remove length", offset); + + DefaultDocumentEvent event = + new DefaultDocumentEvent(offset, length, + DocumentEvent.EventType.REMOVE); + + try + { + writeLock(); - // The order of the operations below is critical! - removeUpdate(event); - UndoableEdit temp = content.remove(offset, length); + // The order of the operations below is critical! + removeUpdate(event); + UndoableEdit temp = content.remove(offset, length); - postRemoveUpdate(event); - fireRemoveUpdate(event); + postRemoveUpdate(event); + fireRemoveUpdate(event); + } + finally + { + writeUnlock(); + } } - finally - { - writeUnlock(); - } } /** @@ -1674,20 +1694,15 @@ public abstract class AbstractDocument implements Document, Serializable /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = -6037216547466333183L; - /** The child elements of this BranchElement. */ - private Element[] children = new Element[0]; - /** - * The cached startOffset value. This is used in the case when a - * BranchElement (temporarily) has no child elements. + * The child elements of this BranchElement. */ - private int startOffset; + private Element[] children;; /** - * The cached endOffset value. This is used in the case when a - * BranchElement (temporarily) has no child elements. + * The number of children in the branch element. */ - private int endOffset; + private int numChildren; /** * Creates a new <code>BranchElement</code> with the specified @@ -1700,8 +1715,8 @@ public abstract class AbstractDocument implements Document, Serializable public BranchElement(Element parent, AttributeSet attributes) { super(parent, attributes); - startOffset = -1; - endOffset = -1; + children = new Element[1]; + numChildren = 0; } /** @@ -1716,8 +1731,8 @@ public abstract class AbstractDocument implements Document, Serializable Vector tmp = new Vector(); - for (int index = 0; index < children.length; ++index) - tmp.add(children[index]); + for (int index = 0; index < numChildren; ++index) + tmp.add(children[index]); return tmp.elements(); } @@ -1743,8 +1758,8 @@ public abstract class AbstractDocument implements Document, Serializable */ public Element getElement(int index) { - if (index < 0 || index >= children.length) - return null; + if (index < 0 || index >= numChildren) + return null; return children[index]; } @@ -1756,7 +1771,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getElementCount() { - return children.length; + return numChildren; } /** @@ -1777,7 +1792,7 @@ public abstract class AbstractDocument implements Document, Serializable // XXX: There is surely a better algorithm // as beginning from first element each time. - for (int index = 0; index < children.length - 1; ++index) + for (int index = 0; index < numChildren - 1; ++index) { Element elem = children[index]; @@ -1814,15 +1829,11 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getEndOffset() { - if (children.length == 0) - { - if (endOffset == -1) - throw new NullPointerException("BranchElement has no children."); - } - else - endOffset = children[children.length - 1].getEndOffset(); - - return endOffset; + // This might accss one cached element or trigger an NPE for + // numChildren == 0. This is checked by a Mauve test. + Element child = numChildren > 0 ? children[numChildren - 1] + : children[0]; + return child.getEndOffset(); } /** @@ -1848,15 +1859,13 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getStartOffset() { - if (children.length == 0) - { - if (startOffset == -1) - throw new NullPointerException("BranchElement has no children."); - } - else - startOffset = children[0].getStartOffset(); - - return startOffset; + // Do not explicitly throw an NPE here. If the first element is null + // then the NPE gets thrown anyway. If it isn't, then it either + // holds a real value (for numChildren > 0) or a cached value + // (for numChildren == 0) as we don't fully remove elements in replace() + // when removing single elements. + // This is checked by a Mauve test. + return children[0].getStartOffset(); } /** @@ -1884,7 +1893,7 @@ public abstract class AbstractDocument implements Document, Serializable { // XXX: There is surely a better algorithm // as beginning from first element each time. - for (int index = 0; index < children.length; ++index) + for (int index = 0; index < numChildren; ++index) { Element elem = children[index]; @@ -1905,14 +1914,26 @@ public abstract class AbstractDocument implements Document, Serializable */ public void replace(int offset, int length, Element[] elements) { - Element[] target = new Element[children.length - length - + elements.length]; - System.arraycopy(children, 0, target, 0, offset); - System.arraycopy(elements, 0, target, offset, elements.length); - System.arraycopy(children, offset + length, target, - offset + elements.length, - children.length - offset - length); - children = target; + int delta = elements.length - length; + int copyFrom = offset + length; // From where to copy. + int copyTo = copyFrom + delta; // Where to copy to. + int numMove = numChildren - copyFrom; // How many elements are moved. + if (numChildren + delta > children.length) + { + // Gotta grow the array. + int newSize = Math.max(2 * children.length, numChildren + delta); + Element[] target = new Element[newSize]; + System.arraycopy(children, 0, target, 0, offset); + System.arraycopy(elements, 0, target, offset, elements.length); + System.arraycopy(children, copyFrom, target, copyTo, numMove); + children = target; + } + else + { + System.arraycopy(children, copyFrom, children, copyTo, numMove); + System.arraycopy(elements, 0, children, offset, elements.length); + } + numChildren += delta; } /** @@ -2165,18 +2186,6 @@ public abstract class AbstractDocument implements Document, Serializable private Position endPos; /** - * This gets possible added to the startOffset when a startOffset - * outside the document range is requested. - */ - private int startDelta; - - /** - * This gets possible added to the endOffset when a endOffset - * outside the document range is requested. - */ - private int endDelta; - - /** * Creates a new <code>LeafElement</code>. * * @param parent the parent of this <code>LeafElement</code> @@ -2188,28 +2197,21 @@ public abstract class AbstractDocument implements Document, Serializable int end) { super(parent, attributes); - int len = content.length(); - startDelta = 0; - if (start > len) - startDelta = start - len; - endDelta = 0; - if (end > len) - endDelta = end - len; try - { - startPos = createPosition(start - startDelta); - endPos = createPosition(end - endDelta); - } - catch (BadLocationException ex) - { - AssertionError as; - as = new AssertionError("BadLocationException thrown " - + "here. start=" + start - + ", end=" + end - + ", length=" + getLength()); - as.initCause(ex); - throw as; - } + { + startPos = createPosition(start); + endPos = createPosition(end); + } + catch (BadLocationException ex) + { + AssertionError as; + as = new AssertionError("BadLocationException thrown " + + "here. start=" + start + + ", end=" + end + + ", length=" + getLength()); + as.initCause(ex); + throw as; + } } /** @@ -2281,7 +2283,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getEndOffset() { - return endPos.getOffset() + endDelta; + return endPos.getOffset(); } /** @@ -2307,7 +2309,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getStartOffset() { - return startPos.getOffset() + startDelta; + return startPos.getOffset(); } /** diff --git a/libjava/classpath/javax/swing/text/BoxView.java b/libjava/classpath/javax/swing/text/BoxView.java index a184a813152..27e3c0f9a1b 100644 --- a/libjava/classpath/javax/swing/text/BoxView.java +++ b/libjava/classpath/javax/swing/text/BoxView.java @@ -67,6 +67,11 @@ public class BoxView private boolean[] layoutValid = new boolean[2]; /** + * Indicates if the requirements for an axis are valid. + */ + private boolean[] requirementsValid = new boolean[2]; + + /** * The spans along the X_AXIS and Y_AXIS. */ private int[][] spans = new int[2][]; @@ -265,8 +270,10 @@ public class BoxView super.replace(offset, length, views); // Invalidate layout information. - layoutChanged(X_AXIS); - layoutChanged(Y_AXIS); + layoutValid[X_AXIS] = false; + requirementsValid[X_AXIS] = false; + layoutValid[Y_AXIS] = false; + requirementsValid[Y_AXIS] = false; } /** @@ -278,19 +285,26 @@ public class BoxView */ public void paint(Graphics g, Shape a) { - Rectangle inside = getInsideAllocation(a); - // TODO: Used for debugging. - //g.drawRect(inside.x, inside.y, inside.width, inside.height); + Rectangle alloc; + if (a instanceof Rectangle) + alloc = (Rectangle) a; + else + alloc = a.getBounds(); + + int x = alloc.x + getLeftInset(); + int y = alloc.y + getTopInset(); - Rectangle copy = new Rectangle(inside); + Rectangle clip = g.getClipBounds(); + Rectangle tmp = new Rectangle(); int count = getViewCount(); for (int i = 0; i < count; ++i) { - copy.setBounds(inside); - childAllocation(i, copy); - if (!copy.isEmpty() - && g.hitClip(copy.x, copy.y, copy.width, copy.height)) - paintChild(g, copy, i); + tmp.x = x + getOffset(X_AXIS, i); + tmp.y = y + getOffset(Y_AXIS, i); + tmp.width = getSpan(X_AXIS, i); + tmp.height = getSpan(Y_AXIS, i); + if (tmp.intersects(clip)) + paintChild(g, tmp, i); } } @@ -305,7 +319,13 @@ public class BoxView public float getPreferredSpan(int axis) { updateRequirements(axis); - return requirements[axis].preferred; + // Add margin. + float margin; + if (axis == X_AXIS) + margin = getLeftInset() + getRightInset(); + else + margin = getTopInset() + getBottomInset(); + return requirements[axis].preferred + margin; } /** @@ -319,12 +339,14 @@ public class BoxView */ public float getMaximumSpan(int axis) { - float max; - if (axis == myAxis) - max = getPreferredSpan(axis); + updateRequirements(axis); + // Add margin. + float margin; + if (axis == X_AXIS) + margin = getLeftInset() + getRightInset(); else - max = Integer.MAX_VALUE; - return max; + margin = getTopInset() + getBottomInset(); + return requirements[axis].maximum + margin; } /** @@ -341,7 +363,13 @@ public class BoxView public float getMinimumSpan(int axis) { updateRequirements(axis); - return requirements[axis].minimum; + // Add margin. + float margin; + if (axis == X_AXIS) + margin = getLeftInset() + getRightInset(); + else + margin = getTopInset() + getBottomInset(); + return requirements[axis].minimum + margin; } /** @@ -435,34 +463,29 @@ public class BoxView protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements sr) { - updateChildRequirements(axis); + SizeRequirements res = sr; + if (res == null) + res = new SizeRequirements(); - SizeRequirements result = sr; - if (result == null) - result = new SizeRequirements(); + float min = 0; + float pref = 0; + float max = 0; - long minimum = 0; - long preferred = 0; - long maximum = 0; - for (int i = 0; i < children.length; i++) + int n = getViewCount(); + for (int i = 0; i < n; i++) { - minimum += childReqs[axis][i].minimum; - preferred += childReqs[axis][i].preferred; - maximum += childReqs[axis][i].maximum; + View child = getView(i); + min += child.getMinimumSpan(axis); + pref = child.getPreferredSpan(axis); + max = child.getMaximumSpan(axis); } - // Overflow check. - if (minimum > Integer.MAX_VALUE) - minimum = Integer.MAX_VALUE; - if (preferred > Integer.MAX_VALUE) - preferred = Integer.MAX_VALUE; - if (maximum > Integer.MAX_VALUE) - maximum = Integer.MAX_VALUE; - - result.minimum = (int) minimum; - result.preferred = (int) preferred; - result.maximum = (int) maximum; - result.alignment = 0.5F; - return result; + + res.minimum = (int) min; + res.preferred = (int) pref; + res.maximum = (int) max; + res.alignment = 0.5F; + + return res; } /** @@ -480,44 +503,24 @@ public class BoxView protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements sr) { - updateChildRequirements(axis); - SizeRequirements res = sr; if (res == null) res = new SizeRequirements(); - float minLeft = 0; - float minRight = 0; - float prefLeft = 0; - float prefRight = 0; - float maxLeft = 0; - float maxRight = 0; - for (int i = 0; i < childReqs[axis].length; i++) + res.minimum = 0; + res.preferred = 0; + res.maximum = 0; + res.alignment = 0.5F; + int n = getViewCount(); + for (int i = 0; i < n; i++) { - float myMinLeft = childReqs[axis][i].minimum * childReqs[axis][i].alignment; - float myMinRight = childReqs[axis][i].minimum - myMinLeft; - minLeft = Math.max(myMinLeft, minLeft); - minRight = Math.max(myMinRight, minRight); - float myPrefLeft = childReqs[axis][i].preferred * childReqs[axis][i].alignment; - float myPrefRight = childReqs[axis][i].preferred - myPrefLeft; - prefLeft = Math.max(myPrefLeft, prefLeft); - prefRight = Math.max(myPrefRight, prefRight); - float myMaxLeft = childReqs[axis][i].maximum * childReqs[axis][i].alignment; - float myMaxRight = childReqs[axis][i].maximum - myMaxLeft; - maxLeft = Math.max(myMaxLeft, maxLeft); - maxRight = Math.max(myMaxRight, maxRight); + View child = getView(i); + res.minimum = Math.max((int) child.getMinimumSpan(axis), res.minimum); + res.preferred = Math.max((int) child.getPreferredSpan(axis), + res.preferred); + res.maximum = Math.max((int) child.getMaximumSpan(axis), res.maximum); } - int minSize = (int) (minLeft + minRight); - int prefSize = (int) (prefLeft + prefRight); - int maxSize = (int) (maxLeft + maxRight); - float align = prefLeft / (prefRight + prefLeft); - if (Float.isNaN(align)) - align = 0; - res.alignment = align; - res.maximum = maxSize; - res.preferred = prefSize; - res.minimum = minSize; return res; } @@ -697,15 +700,62 @@ public class BoxView protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - updateChildRequirements(axis); - updateRequirements(axis); + // Set the spans to the preferred sizes. Determine the space + // that we have to adjust the sizes afterwards. + long sumPref = 0; + int n = getViewCount(); + for (int i = 0; i < n; i++) + { + View child = getView(i); + spans[i] = (int) child.getPreferredSpan(axis); + sumPref = spans[i]; + } - // Calculate the spans and offsets using the SizeRequirements uility - // methods. - SizeRequirements.calculateTiledPositions(targetSpan, requirements[axis], - childReqs[axis], - offsets, spans); + // Try to adjust the spans so that we fill the targetSpan. + long diff = targetSpan - sumPref; + float factor = 0.0F; + int[] diffs = null; + if (diff != 0) + { + long total = 0; + diffs = new int[n]; + for (int i = 0; i < n; i++) + { + View child = getView(i); + int span; + if (diff < 0) + { + span = (int) child.getMinimumSpan(axis); + diffs[i] = spans[i] - span; + } + else + { + span = (int) child.getMaximumSpan(axis); + diffs[i] = span - spans[i]; + } + total += span; + } + float maxAdjust = Math.abs(total - sumPref); + factor = diff / maxAdjust; + factor = Math.min(factor, 1.0F); + factor = Math.max(factor, -1.0F); + } + + // Actually perform adjustments. + int totalOffs = 0; + for (int i = 0; i < n; i++) + { + offsets[i] = totalOffs; + if (diff != 0) + { + float adjust = factor * diffs[i]; + spans[i] += Math.round(adjust); + } + // Avoid overflow here. + totalOffs = (int) Math.min((long) totalOffs + (long) spans[i], + Integer.MAX_VALUE); + } } /** @@ -720,14 +770,26 @@ public class BoxView protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - updateChildRequirements(axis); - updateRequirements(axis); - - // Calculate the spans and offsets using the SizeRequirements uility - // methods. - SizeRequirements.calculateAlignedPositions(targetSpan, requirements[axis], - childReqs[axis], offsets, - spans); + int count = getViewCount(); + for (int i = 0; i < count; i++) + { + View child = getView(i); + int max = (int) child.getMaximumSpan(axis); + if (max < targetSpan) + {System.err.println("align: " + child); + // Align child when it can't be made as wide as the target span. + float align = child.getAlignment(axis); + offsets[i] = (int) ((targetSpan - max) * align); + spans[i] = max; + } + else + { + // Expand child to target width if possible. + int min = (int) child.getMinimumSpan(axis); + offsets[i] = 0; + spans[i] = Math.max(min, targetSpan); + } + } } /** @@ -822,15 +884,8 @@ public class BoxView */ public float getAlignment(int axis) { - float align; - if (axis == myAxis) - align = 0.5F; - else - { - updateRequirements(axis); - align = requirements[axis].alignment; - } - return align; + updateRequirements(axis); + return requirements[axis].alignment; } /** @@ -843,9 +898,15 @@ public class BoxView public void preferenceChanged(View child, boolean width, boolean height) { if (width) - layoutValid[X_AXIS] = false; + { + layoutValid[X_AXIS] = false; + requirementsValid[X_AXIS] = false; + } if (height) - layoutValid[Y_AXIS] = false; + { + layoutValid[Y_AXIS] = false; + requirementsValid[Y_AXIS] = false; + } super.preferenceChanged(child, width, height); } @@ -862,7 +923,7 @@ public class BoxView if (! isAllocationValid()) { Rectangle bounds = a.getBounds(); - layout(bounds.width, bounds.height); + setSize(bounds.width, bounds.height); } return super.modelToView(pos, a, bias); } @@ -963,7 +1024,7 @@ public class BoxView */ private void updateRequirements(int axis) { - if (! layoutValid[axis]) + if (! requirementsValid[axis]) { if (axis == myAxis) requirements[axis] = calculateMajorAxisRequirements(axis, @@ -971,6 +1032,7 @@ public class BoxView else requirements[axis] = calculateMinorAxisRequirements(axis, requirements[axis]); + requirementsValid[axis] = true; } } } diff --git a/libjava/classpath/javax/swing/text/CompositeView.java b/libjava/classpath/javax/swing/text/CompositeView.java index 17f13dbedd6..6f487b8981e 100644 --- a/libjava/classpath/javax/swing/text/CompositeView.java +++ b/libjava/classpath/javax/swing/text/CompositeView.java @@ -217,25 +217,43 @@ public abstract class CompositeView public Shape modelToView(int pos, Shape a, Position.Bias bias) throws BadLocationException { - int childIndex = getViewIndex(pos, bias); - if (childIndex == -1) - throw new BadLocationException("Position " + pos + " is not represented by view.", pos); - - Shape ret = null; - - View child = getView(childIndex); - Shape childAlloc = getChildAllocation(childIndex, a); - - if (childAlloc == null) - ret = createDefaultLocation(a, bias); - - Shape result = child.modelToView(pos, childAlloc, bias); - - if (result != null) - ret = result; - else - ret = createDefaultLocation(a, bias); + boolean backward = bias == Position.Bias.Backward; + int testpos = backward ? Math.max(0, pos - 1) : pos; + Shape ret = null; + if (! backward || testpos >= getStartOffset()) + { + int childIndex = getViewIndexAtPosition(testpos); + if (childIndex != -1 && childIndex < getViewCount()) + { + View child = getView(childIndex); + if (child != null && testpos >= child.getStartOffset() + && testpos < child.getEndOffset()) + { + Shape childAlloc = getChildAllocation(childIndex, a); + if (childAlloc != null) + { + ret = child.modelToView(pos, childAlloc, bias); + // Handle corner case. + if (ret == null && child.getEndOffset() == pos) + { + childIndex++; + if (childIndex < getViewCount()) + { + child = getView(childIndex); + childAlloc = getChildAllocation(childIndex, a); + ret = child.modelToView(pos, childAlloc, bias); + } + } + } + } + } + else + { + throw new BadLocationException("Position " + pos + + " is not represented by view.", pos); + } + } return ret; } @@ -378,7 +396,10 @@ public abstract class CompositeView { if (b == Position.Bias.Backward && pos != 0) pos -= 1; - return getViewIndexAtPosition(pos); + int i = -1; + if (pos >= getStartOffset() && pos < getEndOffset()) + i = getViewIndexAtPosition(pos); + return i; } /** @@ -446,9 +467,13 @@ public abstract class CompositeView */ protected View getViewAtPosition(int pos, Rectangle a) { + View view = null; int i = getViewIndexAtPosition(pos); - View view = children[i]; - childAllocation(i, a); + if (i >= 0 && i < getViewCount() && a != null) + { + view = getView(i); + childAllocation(i, a); + } return view; } @@ -464,17 +489,10 @@ public abstract class CompositeView */ protected int getViewIndexAtPosition(int pos) { - int index = -1; - for (int i = 0; i < children.length; i++) - { - if (children[i].getStartOffset() <= pos - && children[i].getEndOffset() > pos) - { - index = i; - break; - } - } - return index; + // We have a 1:1 mapping of elements to views here, so we forward + // this to the element. + Element el = getElement(); + return el.getElementIndex(pos); } /** diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java index 4ad204c00c9..84f47f120de 100644 --- a/libjava/classpath/javax/swing/text/DefaultCaret.java +++ b/libjava/classpath/javax/swing/text/DefaultCaret.java @@ -221,9 +221,12 @@ public class DefaultCaret extends Rectangle if (name.equals("document")) { Document oldDoc = (Document) e.getOldValue(); - oldDoc.removeDocumentListener(documentListener); + if (oldDoc != null) + oldDoc.removeDocumentListener(documentListener); + Document newDoc = (Document) e.getNewValue(); - newDoc.addDocumentListener(documentListener); + if (newDoc != null) + newDoc.addDocumentListener(documentListener); } else if (name.equals("editable")) { @@ -549,7 +552,6 @@ public class DefaultCaret extends Rectangle */ public void mousePressed(MouseEvent event) { - int button = event.getButton(); // The implementation assumes that consuming the event makes the AWT event // mechanism forget about this event instance and not transfer focus. @@ -562,23 +564,37 @@ public class DefaultCaret extends Rectangle // - a middle-click positions the caret and pastes the clipboard // contents. // - a middle-click when shift is held down is ignored - - if (button == MouseEvent.BUTTON1) - if (event.isShiftDown()) - moveCaret(event); - else - positionCaret(event); - else if(button == MouseEvent.BUTTON2) - if (event.isShiftDown()) - event.consume(); + + if (SwingUtilities.isLeftMouseButton(event)) + { + // Handle the caret. + if (event.isShiftDown() && getDot() != -1) + { + moveCaret(event); + } else { positionCaret(event); - + } + + // Handle the focus. + if (textComponent != null && textComponent.isEnabled() + && textComponent.isRequestFocusEnabled()) + { + textComponent.requestFocus(); + } + + // TODO: Handle double click for selecting words. + } + else if(event.getButton() == MouseEvent.BUTTON2) + { + // Special handling for X11-style pasting. + if (! event.isShiftDown()) + { + positionCaret(event); textComponent.paste(); } - else - event.consume(); + } } /** @@ -708,7 +724,11 @@ public class DefaultCaret extends Rectangle propertyChangeListener = new PropertyChangeHandler(); textComponent.addPropertyChangeListener(propertyChangeListener); documentListener = new DocumentHandler(); - textComponent.getDocument().addDocumentListener(documentListener); + + Document doc = textComponent.getDocument(); + if (doc != null) + doc.addDocumentListener(documentListener); + active = textComponent.isEditable() && textComponent.isEnabled(); repaint(); @@ -891,10 +911,10 @@ public class DefaultCaret extends Rectangle } catch (BadLocationException e) { - AssertionError ae; - ae = new AssertionError("Unexpected bad caret location: " + dot); - ae.initCause(e); - throw ae; + // Let's ignore that. This shouldn't really occur. But if it + // does (it seems that this happens when the model is mutating), + // it causes no real damage. Uncomment this for debugging. + // e.printStackTrace(); } if (rect == null) @@ -1128,10 +1148,10 @@ public class DefaultCaret extends Rectangle } catch (BadLocationException e) { - AssertionError ae; - ae = new AssertionError("Unexpected bad caret location: " + dot); - ae.initCause(e); - throw ae; + // Let's ignore that. This shouldn't really occur. But if it + // does (it seems that this happens when the model is mutating), + // it causes no real damage. Uncomment this for debugging. + // e.printStackTrace(); } if (area != null) damage(area); @@ -1140,6 +1160,24 @@ public class DefaultCaret extends Rectangle } /** + * Returns <code>true</code> if this <code>Caret</code> is blinking, + * and <code>false</code> if not. The returned value is independent of + * the visiblity of this <code>Caret</code> as returned by {@link #isVisible()}. + * + * @return <code>true</code> if this <code>Caret</code> is blinking, + * and <code>false</code> if not. + * @see #isVisible() + * @since 1.5 + */ + public boolean isActive() + { + if (blinkTimer != null) + return blinkTimer.isRunning(); + + return false; + } + + /** * Returns <code>true</code> if this <code>Caret</code> is currently visible, * and <code>false</code> if it is not. * diff --git a/libjava/classpath/javax/swing/text/EmptyAttributeSet.java b/libjava/classpath/javax/swing/text/EmptyAttributeSet.java new file mode 100644 index 00000000000..98fb8828c89 --- /dev/null +++ b/libjava/classpath/javax/swing/text/EmptyAttributeSet.java @@ -0,0 +1,153 @@ +/* EmptyAttributeSet.java -- An empty attribute set + 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 javax.swing.text; + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +/** + * An immutable, empty attribute set. + * + * @see SimpleAttributeSet#EMPTY + * + * @author Roman Kennke (kennke@aicas.com) + */ +final class EmptyAttributeSet + implements AttributeSet +{ + + /** + * Always return false as this AttributeSet doesn't contain any attributes. + */ + public boolean containsAttribute(Object name, Object value) + { + return false; + } + + /** + * Return true only if the attributes argument also contains no attributes. + */ + public boolean containsAttributes(AttributeSet attributes) + { + return attributes.getAttributeCount() == 0; + } + + /** + * Return this, as this is immutable. + */ + public AttributeSet copyAttributes() + { + return this; + } + + /** + * Always return null as this AttributeSet doesn't contain any attributes. + */ + public Object getAttribute(Object key) + { + return null; + } + + /** + * Always return 0. + */ + public int getAttributeCount() + { + return 0; + } + + /** + * Returns an empty Enumeration. + */ + public Enumeration getAttributeNames() + { + return new Enumeration() + { + public boolean hasMoreElements() + { + return false; + } + + public Object nextElement() + { + throw new NoSuchElementException("No more elements"); + } + + }; + } + + /** + * Always return null as this has no resolve parent. + */ + public AttributeSet getResolveParent() + { + return null; + } + + /** + * Always return false as this AttributeSet doesn't contain any attributes. + */ + public boolean isDefined(Object attrName) + { + return false; + } + + /** + * Other attribute sets are equal if they are empty too. + */ + public boolean isEqual(AttributeSet attr) + { + return attr.getAttributeCount() == 0; + } + + /** + * Other objects are equal if it's the same instance as this, or if + * it's another attribute set without attributes. + */ + public boolean equals(Object o) + { + boolean eq = o == this; + if (! eq) + { + eq = (o instanceof AttributeSet) + && ((AttributeSet) o).getAttributeCount() == 0; + } + return eq; + } +} diff --git a/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java index 8ca55d8347a..3de95ed7f8d 100644 --- a/libjava/classpath/javax/swing/text/FlowView.java +++ b/libjava/classpath/javax/swing/text/FlowView.java @@ -159,20 +159,18 @@ public abstract class FlowView extends BoxView } /** - * Lays out one row of the flow view. This is called by {@link #layout} - * to fill one row with child views until the available span is exhausted. - * - * The default implementation fills the row by calling - * {@link #createView(FlowView, int, int, int)} until the available space - * is exhausted, a forced break is encountered or there are no more views - * in the logical view. If the available space is exhausted, - * {@link #adjustRow(FlowView, int, int, int)} is called to fit the row - * into the available span. - * + * Lays out one row of the flow view. This is called by {@link #layout} to + * fill one row with child views until the available span is exhausted. The + * default implementation fills the row by calling + * {@link #createView(FlowView, int, int, int)} until the available space is + * exhausted, a forced break is encountered or there are no more views in + * the logical view. If the available space is exhausted, + * {@link #adjustRow(FlowView, int, int, int)} is called to fit the row into + * the available span. + * * @param fv the flow view for which we perform the layout * @param rowIndex the index of the row * @param pos the model position for the beginning of the row - * * @return the start position of the next row */ protected int layoutRow(FlowView fv, int rowIndex, int pos) @@ -188,34 +186,39 @@ public abstract class FlowView extends BoxView if (span == 0) span = Integer.MAX_VALUE; - while (span > 0) + Row: while (span > 0) { - if (logicalView.getViewIndex(offset, Position.Bias.Forward) == -1) + if (logicalView.getViewIndex(offset, Position.Bias.Forward) == - 1) break; View view = createView(fv, offset, span, rowIndex); if (view == null) break; + int viewSpan = (int) view.getPreferredSpan(axis); - row.append(view); int breakWeight = view.getBreakWeight(axis, x, span); - if (breakWeight >= View.ForcedBreakWeight) - break; + + row.append(view); + offset += (view.getEndOffset() - view.getStartOffset()); x += viewSpan; span -= viewSpan; - offset += (view.getEndOffset() - view.getStartOffset()); - } - if (span < 0) - { - int flowStart = fv.getFlowStart(axis); - int flowSpan = fv.getFlowSpan(axis); - adjustRow(fv, rowIndex, flowSpan, flowStart); - int rowViewCount = row.getViewCount(); - if (rowViewCount > 0) - offset = row.getView(rowViewCount - 1).getEndOffset(); - else - offset = -1; + + // Break if the line if the view does not fit in this row or the + // line just must be broken. + if (span < 0 || breakWeight >= View.ForcedBreakWeight) + { + int flowStart = fv.getFlowStart(axis); + int flowSpan = fv.getFlowSpan(axis); + adjustRow(fv, rowIndex, flowSpan, flowStart); + int rowViewCount = row.getViewCount(); + if (rowViewCount > 0) + offset = row.getView(rowViewCount - 1).getEndOffset(); + else + offset = - 1; + break Row; + } } - return offset != pos ? offset : -1; + + return offset != pos ? offset : - 1; } /** @@ -521,6 +524,7 @@ public abstract class FlowView extends BoxView */ public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf) { + layoutPool.removeUpdate(changes, a, vf); strategy.removeUpdate(this, changes, getInsideAllocation(a)); layoutDirty = true; } @@ -536,6 +540,7 @@ public abstract class FlowView extends BoxView */ public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf) { + layoutPool.changedUpdate(changes, a, vf); strategy.changedUpdate(this, changes, getInsideAllocation(a)); layoutDirty = true; } @@ -594,12 +599,14 @@ public abstract class FlowView extends BoxView protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) { - // We need to call super here so that the alignment is properly - // calculated. - SizeRequirements res = super.calculateMinorAxisRequirements(axis, r); + SizeRequirements res = r; + if (res == null) + res = new SizeRequirements(); res.minimum = (int) layoutPool.getMinimumSpan(axis); - res.preferred = (int) layoutPool.getPreferredSpan(axis); - res.maximum = (int) layoutPool.getMaximumSpan(axis); + res.preferred = Math.max(res.minimum, + (int) layoutPool.getMinimumSpan(axis)); + res.maximum = Integer.MAX_VALUE; + res.alignment = 0.5F; return res; } } diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java index 4f06003b458..760e396a223 100644 --- a/libjava/classpath/javax/swing/text/GapContent.java +++ b/libjava/classpath/javax/swing/text/GapContent.java @@ -39,7 +39,13 @@ exception statement from your version. */ package javax.swing.text; import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Set; import java.util.Vector; import java.util.WeakHashMap; @@ -73,30 +79,39 @@ public class GapContent * The index to the positionMarks array entry, which in turn holds the * mark into the buffer array. */ - int index; + Mark mark; /** * Creates a new GapContentPosition object. * - * @param mark the mark of this Position + * @param offset the offset of this Position */ - GapContentPosition(int mark) + GapContentPosition(int offset) { // Try to find the mark in the positionMarks array, and store the index // to it. synchronized (GapContent.this) { - int i = binarySearch(positionMarks, mark, numMarks); + // Try to make space. + garbageCollect(); + Mark m = new Mark(offset); + int i = search(marks, m); if (i >= 0) // mark found { - index = i; + m = (Mark) marks.get(i); } else { - index = -i - 1; - insertMark(index, mark); + i = -i - 1; + marks.add(i, m); } + m.refCount++; + mark = m; } + + // Register this position in the death queue, so we can cleanup the marks + // when this position object gets GC'ed. + new WeakReference(this, queueOfDeath); } /** @@ -106,19 +121,77 @@ public class GapContent */ public int getOffset() { - synchronized (GapContent.this) - { - // Fetch the actual mark. - int mark = positionMarks[index]; - // Check precondition. - assert mark <= gapStart || mark >= gapEnd : "mark: " + mark - + ", gapStart: " + gapStart - + ", gapEnd: " + gapEnd; - int res = mark; - if (mark > gapStart) - res -= (gapEnd - gapStart); - return res; - } + return mark.getOffset(); + } + } + + /** + * Holds a mark into the buffer that is used by GapContentPosition to find + * the actual offset of the position. This is pulled out of the + * GapContentPosition object so that the mark and position can be handled + * independently, and most important, so that the GapContentPosition can + * be garbage collected while we still hold a reference to the Mark object. + */ + private class Mark + implements Comparable + { + /** + * The actual mark into the buffer. + */ + int mark; + + /** + * The number of GapContentPosition object that reference this mark. If + * it reaches zero, it get's deleted by {@link GapContent#garbageCollect()}. + */ + int refCount; + + /** + * Creates a new Mark object for the specified offset. + * + * @param offset the offset + */ + Mark(int offset) + { + mark = offset; + if (mark >= gapStart && mark != 0) + mark += (gapEnd - gapStart); + } + + /** + * Returns the offset of the mark. + * + * @return the offset of the mark + */ + int getOffset() + { + assert mark == 0 || mark < gapStart || mark >= gapEnd : + "Invalid mark: " + mark + ", gapStart: " + gapStart + + ", gapEnd: " + gapEnd; + + int res = mark; + if (mark >= gapEnd) + res -= (gapEnd - gapStart); + return res; + } + + /** + * Implementation of Comparable. + */ + public int compareTo(Object o) + { + Mark other = (Mark) o; + return mark - other.mark; + } + /** + * Adjustment for equals(). + */ + public boolean equals(Object o) + { + if (o == null || !(o instanceof Mark)) + return false; + else + return ((Mark) o).mark == mark; } } @@ -230,19 +303,21 @@ public class GapContent /** * Holds the marks for positions. These marks are referenced by the * GapContentPosition instances by an index into this array. + * + * This is package private to avoid accessor synthetic methods. */ - int[] positionMarks; + ArrayList marks; - /** - * The number of elements in the positionMarks array. The positionMarks array - * might be bigger than the actual number of elements. - */ - int numMarks; + WeakHashMap positions; /** - * (Weakly) Stores the GapContentPosition instances. + * Queues all references to GapContentPositions that are about to be + * GC'ed. This is used to remove the corresponding marks from the + * positionMarks array if the number of references to that mark reaches zero. + * + * This is package private to avoid accessor synthetic methods. */ - WeakHashMap positions; + ReferenceQueue queueOfDeath; /** * Creates a new GapContent object. @@ -265,8 +340,8 @@ public class GapContent gapEnd = size; buffer[0] = '\n'; positions = new WeakHashMap(); - positionMarks = new int[10]; - numMarks = 0; + marks = new ArrayList(); + queueOfDeath = new ReferenceQueue(); } /** @@ -417,6 +492,8 @@ public class GapContent if ((where + len) > length) throw new BadLocationException("len plus where cannot be greater" + " than the content length", len + where); + if (len < 0) + throw new BadLocationException("negative length not allowed: ", len); // check if requested segment is contiguous if ((where < gapStart) && ((gapStart - where) < len)) @@ -455,6 +532,11 @@ public class GapContent */ public Position createPosition(final int offset) throws BadLocationException { + // Implementation note: We used to perform explicit check on the offset + // here. However, this makes some Mauve and Intel/Harmony tests fail + // and luckily enough the GapContent can very well deal with offsets + // outside the buffer bounds. So I removed that check. + // We try to find a GapContentPosition at the specified offset and return // that. Otherwise we must create a new one. GapContentPosition pos = null; @@ -472,10 +554,7 @@ public class GapContent // If none was found, then create and return a new one. if (pos == null) { - int mark = offset; - if (mark >= gapStart) - mark += (gapEnd - gapStart); - pos = new GapContentPosition(mark); + pos = new GapContentPosition(offset); positions.put(pos, null); } @@ -497,7 +576,7 @@ public class GapContent int delta = newSize - gapEnd + gapStart; // Update the marks after the gapEnd. - adjustPositionsInRange(gapEnd, buffer.length - gapEnd, delta); + adjustPositionsInRange(gapEnd, -1, delta); // Copy the data around. char[] newBuf = (char[]) allocateArray(length() + newSize); @@ -523,7 +602,7 @@ public class GapContent { // Update the positions between newGapStart and (old) gapStart. The marks // must be shifted by (gapEnd - gapStart). - adjustPositionsInRange(newGapStart, gapStart - newGapStart, gapEnd - gapStart); + adjustPositionsInRange(newGapStart, gapStart, gapEnd - gapStart); System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart - newGapStart); gapStart = newGapStart; @@ -533,14 +612,13 @@ public class GapContent { // Update the positions between newGapEnd and (old) gapEnd. The marks // must be shifted by (gapEnd - gapStart). - adjustPositionsInRange(gapEnd, newGapEnd - gapEnd, -(gapEnd - gapStart)); + adjustPositionsInRange(gapEnd, newGapEnd, -(gapEnd - gapStart)); System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart - gapStart); gapStart = newGapStart; gapEnd = newGapEnd; } - if (gapStart == 0) - resetMarksAtZero(); + resetMarksAtZero(); } /** @@ -560,6 +638,7 @@ public class GapContent + "old gap start."; setPositionsInRange(newGapStart, gapStart, false); gapStart = newGapStart; + resetMarksAtZero(); } /** @@ -579,6 +658,7 @@ public class GapContent + "old gap end."; setPositionsInRange(gapEnd, newGapEnd, false); gapEnd = newGapEnd; + resetMarksAtZero(); } /** @@ -617,10 +697,6 @@ public class GapContent if (addItems != null) { System.arraycopy(addItems, 0, buffer, gapStart, addSize); - - - resetMarksAtZero(); - gapStart += addSize; } } @@ -689,102 +765,61 @@ public class GapContent */ private void setPositionsInRange(int start, int end, boolean toStart) { - // We slump together all the GapContentPositions to point to - // one mark. So this is implemented as follows: - // 1. Remove all the marks in the specified range. - // 2. Insert one new mark at the correct location. - // 3. Adjust all affected GapContentPosition instances to point to - // this new mark. - synchronized (this) { - int startIndex = binarySearch(positionMarks, start, numMarks); + // Find the start and end indices in the positionMarks array. + Mark m = new Mark(0); // For comparison / search only. + m.mark = start; + int startIndex = search(marks, m); if (startIndex < 0) // Translate to insertion index, if not found. startIndex = - startIndex - 1; - int endIndex = binarySearch(positionMarks, end, numMarks); + m.mark = end; + int endIndex = search(marks, m); if (endIndex < 0) // Translate to insertion index - 1, if not found. endIndex = - endIndex - 2; - // Update the marks. - // We have inclusive interval bounds, but let one element over for - // filling in the new value. - int removed = endIndex - startIndex; - if (removed <= 0) - return; - System.arraycopy(positionMarks, endIndex + 1, positionMarks, - startIndex + 1, positionMarks.length - endIndex - 1); - numMarks -= removed; - if (toStart) - { - positionMarks[startIndex] = start; - } - else - { - positionMarks[startIndex] = end; - } + // Actually adjust the marks. + for (int i = startIndex; i <= endIndex; i++) + ((Mark) marks.get(i)).mark = toStart ? start : end; + } - // Update all affected GapContentPositions to point to the new index - // and all GapContentPositions that come after the interval to - // have their index moved by -removed. - Set positionSet = positions.keySet(); - for (Iterator i = positionSet.iterator(); i.hasNext();) - { - GapContentPosition p = (GapContentPosition) i.next(); - if (p.index > startIndex || p.index <= endIndex) - p.index = startIndex; - else if (p.index > endIndex) - p.index -= removed; - } - } } - + /** * Adjusts the mark of all <code>Position</code>s that are in the range * specified by <code>offset</code> and </code>length</code> within * the buffer array by <code>increment</code> * - * @param offset the start offset of the range to search - * @param length the length of the range to search + * @param startOffs the start offset of the range to search + * @param endOffs the length of the range to search, -1 means all to the end * @param incr the increment */ - private void adjustPositionsInRange(int offset, int length, int incr) + private void adjustPositionsInRange(int startOffs, int endOffs, int incr) { - int endMark = offset + length; - synchronized (this) { // Find the start and end indices in the positionMarks array. - int startIndex = binarySearch(positionMarks, offset, numMarks); + Mark m = new Mark(0); // For comparison / search only. + + m.mark = startOffs; + int startIndex = search(marks, m); if (startIndex < 0) // Translate to insertion index, if not found. startIndex = - startIndex - 1; - int endIndex = binarySearch(positionMarks, endMark, numMarks); - if (endIndex < 0) // Translate to insertion index - 1, if not found. - endIndex = - endIndex - 2; - - // We must not change the order of the marks, this would have - // unpredictable results while binary-searching the marks. - assert (startIndex <= 0 - || positionMarks[startIndex - 1] - <= positionMarks [startIndex] + incr) - && (endIndex >= numMarks - 1 - || positionMarks[endIndex + 1] - >= positionMarks[endIndex] + incr) - : "Adjusting the marks must not change their order"; - - // Some debug helper output to determine if the start or end of the - // should ever be coalesced together with adjecent marks. - if (startIndex > 0 && positionMarks[startIndex - 1] - == positionMarks[startIndex] + incr) - System.err.println("DEBUG: We could coalesce the start of the region" - + " in GapContent.adjustPositionsInRange()"); - if (endIndex < numMarks - 1 && positionMarks[endIndex + 1] - == positionMarks[endIndex] + incr) - System.err.println("DEBUG: We could coalesce the end of the region" - + " in GapContent.adjustPositionsInRange()"); + m.mark = endOffs; + int endIndex; + if (endOffs == -1) + endIndex = marks.size() - 1; + else + { + endIndex = search(marks, m); + if (endIndex < 0) // Translate to insertion index - 1, if not found. + endIndex = - endIndex - 2; + } // Actually adjust the marks. - for (int i = startIndex; i <= endIndex; i++) - positionMarks[i] += incr; + for (int i = startIndex; i <= endIndex; i++) { + ((Mark) marks.get(i)).mark += incr; + } } } @@ -800,7 +835,12 @@ public class GapContent if (gapStart != 0) return; - positionMarks[0] = 0; + for (int i = 0; i < marks.size(); i++) + { + Mark m = (Mark) marks.get(i); + if (m.mark <= gapEnd) + m.mark = 0; + } } /** @@ -845,89 +885,60 @@ public class GapContent */ private void dumpMarks() { - System.err.print("positionMarks: "); - for (int i = 0; i < numMarks; i++) - System.err.print(positionMarks[i] + ", "); - System.err.println(); + System.out.print("positionMarks: "); + for (int i = 0; i < marks.size(); i++) + System.out.print(((Mark) marks.get(i)).mark + ", "); + System.out.println(); } /** - * Inserts a mark into the positionMarks array. This must update all the - * GapContentPosition instances in positions that come after insertionPoint. + * Polls the queue of death for GapContentPositions, updates the + * corresponding reference count and removes the corresponding mark + * if the refcount reaches zero. * - * This is package private to avoid synthetic accessor methods. - * - * @param insertionPoint the index at which to insert the mark - * @param mark the mark to insert + * This is package private to avoid accessor synthetic methods. */ - void insertMark(int insertionPoint, int mark) + void garbageCollect() { - synchronized (this) + Reference ref = queueOfDeath.poll(); + while (ref != null) { - // Update the positions. - Set positionSet = positions.keySet(); - for (Iterator i = positionSet.iterator(); i.hasNext();) - { - GapContentPosition p = (GapContentPosition) i.next(); - if (p.index >= insertionPoint) - p.index++; - } - - // Update the position marks. - if (positionMarks.length <= numMarks) + if (ref != null) { - int[] newMarks = new int[positionMarks.length + 10]; - System.arraycopy(positionMarks, 0, newMarks, 0, insertionPoint); - newMarks[insertionPoint] = mark; - System.arraycopy(positionMarks, insertionPoint, newMarks, - insertionPoint + 1, - numMarks - insertionPoint); - positionMarks = newMarks; + GapContentPosition pos = (GapContentPosition) ref.get(); + Mark m = pos.mark; + m.refCount--; + if (m.refCount == 0) + marks.remove(m); } - else - { - System.arraycopy(positionMarks, insertionPoint, positionMarks, - insertionPoint + 1, - numMarks - insertionPoint); - positionMarks[insertionPoint] = mark; - } - numMarks++; + ref = queueOfDeath.poll(); } } /** - * An adaption of {@link java.util.Arrays#binarySearch(int[], int)} to - * specify a maximum index up to which the array is searched. This allows - * us to have some trailing entries that we ignore. - * - * This is package private to avoid synthetic accessor methods. - * - * @param a the array - * @param key the key to search for - * @param maxIndex the maximum index up to which the search is performed + * Searches the first occurance of object <code>o</code> in list + * <code>l</code>. This performs a binary search by calling + * {@link Collections#binarySearch(List, Object)} and when an object has been + * found, it searches backwards to the first occurance of that object in the + * list. The meaning of the return value is the same as in + * <code>Collections.binarySearch()</code>. * - * @return the index of the found entry, or (-(index)-1) for the - * insertion point when not found + * @param l the list to search through + * @param o the object to be searched * - * @see java.util.Arrays#binarySearch(int[], int) + * @return the index of the first occurance of o in l, or -i + 1 if not found */ - int binarySearch(int[] a, int key, int maxIndex) + private int search(List l, Object o) { - int low = 0; - int hi = maxIndex - 1; - int mid = 0; - while (low <= hi) + int i = Collections.binarySearch(l, o); + while (i > 0) { - mid = (low + hi) >>> 1; - final int d = a[mid]; - if (d == key) - return mid; - else if (d > key) - hi = mid - 1; + Object o2 = l.get(i - 1); + if (o2.equals(o)) + i--; else - // This gets the insertion point right on the last loop. - low = ++mid; + break; } - return -mid - 1; + return i; } } diff --git a/libjava/classpath/javax/swing/text/IconView.java b/libjava/classpath/javax/swing/text/IconView.java index 699cda90eba..7bb7635b4e7 100644 --- a/libjava/classpath/javax/swing/text/IconView.java +++ b/libjava/classpath/javax/swing/text/IconView.java @@ -44,7 +44,6 @@ import java.awt.Shape; import javax.swing.Icon; import javax.swing.JTextPane; -import javax.swing.SwingConstants; /** * A View that can render an icon. This view is created by the @@ -156,4 +155,21 @@ public class IconView return el.getStartOffset(); } + /** + * Returns the alignment for this view. This will be 1.0 for the Y_AXIS, + * and the super behaviour for the X_AXIS. + * + * @param axis the axis for which to calculate the alignment + * + * @return the alignment + */ + public float getAlignment(int axis) + { + float align; + if (axis == Y_AXIS) + align = 1.0F; + else + align = super.getAlignment(axis); + return align; + } } diff --git a/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java index 9de151dfbac..6da84bfe7d8 100644 --- a/libjava/classpath/javax/swing/text/JTextComponent.java +++ b/libjava/classpath/javax/swing/text/JTextComponent.java @@ -42,6 +42,7 @@ import gnu.classpath.NotImplementedException; import java.awt.AWTEvent; import java.awt.Color; +import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.Point; @@ -90,9 +91,10 @@ public abstract class JTextComponent extends JComponent implements Scrollable, Accessible { /** - * This class implements accessibility support for the JTextComponent class. - * It provides an implementation of the Java Accessibility API appropriate - * to menu user-interface elements. + * AccessibleJTextComponent implements accessibility hooks for + * JTextComponent. It allows an accessibility driver to read and + * manipulate the text component's contents as well as update UI + * elements such as the caret. */ public class AccessibleJTextComponent extends AccessibleJComponent implements AccessibleText, CaretListener, DocumentListener, AccessibleAction, @@ -100,15 +102,18 @@ public abstract class JTextComponent extends JComponent { private static final long serialVersionUID = 7664188944091413696L; - /** The caret's offset. */ + /** + * The caret's offset. + */ int dot = 0; - - /** The current JTextComponent. */ + + /** + * The current JTextComponent. + */ JTextComponent textComp = JTextComponent.this; - + /** - * Constructs an AccessibleJTextComponent. - * Adds a listener to track caret change. + * Construct an AccessibleJTextComponent. */ public AccessibleJTextComponent() { @@ -117,11 +122,10 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the zero-based offset of the caret. Note: The character - * to the right of the caret will have the same index value as the - * offset (the caret is between two characters). - * - * @return offset of caret + * Retrieve the current caret position. The index of the first + * caret position is 0. + * + * @return caret position */ public int getCaretPosition() { @@ -130,9 +134,10 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the portion of the text that is selected. - * - * @return null if no text is selected. + * Retrieve the current text selection. If no text is selected + * this method returns null. + * + * @return the currently selected text or null */ public String getSelectedText() { @@ -140,11 +145,14 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the start offset within the selected text. If there is no - * selection, but there is a caret, the start and end offsets will be - * the same. Return 0 if the text is empty, or the caret position if no selection. - * - * @return index of the start of the text >= 0. + * Retrieve the index of the first character in the current text + * selection. If there is no text in the text component, this + * method returns 0. If there is text in the text component, but + * there is no selection, this method returns the current caret + * position. + * + * @return the index of the first character in the selection, the + * current caret position or 0 */ public int getSelectionStart() { @@ -154,12 +162,14 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the end offset within the selected text. If there is no - * selection, but there is a caret, the start and end offsets will - * be the same. Return 0 if the text is empty, or the caret position - * if no selection. - * - * @return index of the end of the text >= 0. + * Retrieve the index of the last character in the current text + * selection. If there is no text in the text component, this + * method returns 0. If there is text in the text component, but + * there is no selection, this method returns the current caret + * position. + * + * @return the index of the last character in the selection, the + * current caret position or 0 */ public int getSelectionEnd() { @@ -169,13 +179,10 @@ public abstract class JTextComponent extends JComponent } /** - * Handles caret updates (fire appropriate property change event, which are - * AccessibleContext.ACCESSIBLE_CARET_PROPERTY and - * AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY). This keeps track of - * the dot position internally. When the caret moves, the internal position - * is updated after firing the event. - * - * @param e - caret event + * Handle a change in the caret position and fire any applicable + * property change events. + * + * @param e - the caret update event */ public void caretUpdate(CaretEvent e) throws NotImplementedException @@ -185,7 +192,7 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the accessible state set of this component. + * Retreive the accessible state set of this component. * * @return the accessible state set of this component */ @@ -198,7 +205,7 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the accessible role of this component. + * Retrieve the accessible role of this component. * * @return the accessible role of this component * @@ -210,20 +217,19 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the AccessibleEditableText interface for this text component. - * + * Retrieve an AccessibleEditableText object that controls this + * text component. + * * @return this */ public AccessibleEditableText getAccessibleEditableText() { return this; } - + /** - * Get the AccessibleText associated with this object. In the implementation - * of the Java Accessibility API for this class, return this object, - * which is responsible for implementing the AccessibleText interface on - * behalf of itself. + * Retrieve an AccessibleText object that controls this text + * component. * * @return this * @@ -235,10 +241,11 @@ public abstract class JTextComponent extends JComponent } /** - * Insert update. Fire appropriate property change event which - * is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY. - * - * @param e - document event + * Handle a text insertion event and fire an + * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change + * event. + * + * @param e - the insertion event */ public void insertUpdate(DocumentEvent e) throws NotImplementedException @@ -247,10 +254,11 @@ public abstract class JTextComponent extends JComponent } /** - * Remove update. Fire appropriate property change event which - * is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY. - * - * @param e - document event + * Handle a text removal event and fire an + * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change + * event. + * + * @param e - the removal event */ public void removeUpdate(DocumentEvent e) throws NotImplementedException @@ -259,10 +267,11 @@ public abstract class JTextComponent extends JComponent } /** - * Changed update. Fire appropriate property change event which - * is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY. - * - * @param e - document event + * Handle a text change event and fire an + * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change + * event. + * + * @param e - text change event */ public void changedUpdate(DocumentEvent e) throws NotImplementedException @@ -271,11 +280,13 @@ public abstract class JTextComponent extends JComponent } /** - * Given a point in the coordinate system of this object, return the - * 0-based index of the character at that point, or -1 if there is none. + * Get the index of the character at the given point, in component + * pixel co-ordinates. If the point argument is invalid this + * method returns -1. * - * @param p the point to look at - * @return the character index, or -1 + * @param p - a point in component pixel co-ordinates + * + * @return a character index, or -1 */ public int getIndexAtPoint(Point p) throws NotImplementedException @@ -284,17 +295,14 @@ public abstract class JTextComponent extends JComponent } /** - * Determines the bounding box of the indexed character. Returns an empty - * rectangle if the index is out of bounds. The bounds are returned in local coordinates. - * If the index is invalid a null rectangle is returned. The screen coordinates returned are - * "unscrolled coordinates" if the JTextComponent is contained in a JScrollPane in which - * case the resulting rectangle should be composed with the parent coordinates. - * Note: the JTextComponent must have a valid size (e.g. have been added to a parent - * container whose ancestor container is a valid top-level window) for this method to - * be able to return a meaningful (non-null) value. + * Calculate the bounding box of the character at the given index. + * The returned x and y co-ordinates are relative to this text + * component's top-left corner. If the index is invalid this + * method returns null. + * + * @param index - the character index * - * @param index the 0-based character index - * @return the bounding box, may be empty or null. + * @return a character's bounding box, or null */ public Rectangle getCharacterBounds(int index) throws NotImplementedException @@ -303,9 +311,9 @@ public abstract class JTextComponent extends JComponent } /** - * Return the number of characters. + * Return the length of the text in this text component. * - * @return the character count + * @return a character length */ public int getCharCount() { @@ -313,10 +321,11 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the attributes of a character at an index, or null if the index - * is out of bounds. + * Gets the character attributes of the character at index. If + * the index is out of bounds, null is returned. * - * @param index the 0-based character index + * @param index - index of the character + * * @return the character's attributes */ public AttributeSet getCharacterAttribute(int index) @@ -326,26 +335,28 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the section of text at the index, or null if the index or part - * is invalid. - * - * @param part {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} - * @param index the 0-based character index - * @return the selection of text at that index, or null + * Gets the text located at index. null is returned if the index + * or part is invalid. + * + * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} + * @param index - index of the part + * + * @return the part of text at that index, or null */ public String getAtIndex(int part, int index) throws NotImplementedException { return null; // TODO } - + /** - * Returns the section of text after the index, or null if the index or part - * is invalid. - * - * @param part {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} - * @param index the 0-based character index - * @return the selection of text after that index, or null + * Gets the text located after index. null is returned if the index + * or part is invalid. + * + * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} + * @param index - index after the part + * + * @return the part of text after that index, or null */ public String getAfterIndex(int part, int index) throws NotImplementedException @@ -354,12 +365,13 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the section of text before the index, or null if the index or part - * is invalid. - * - * @param part {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} - * @param index the 0-based character index - * @return the selection of text before that index, or null + * Gets the text located before index. null is returned if the index + * or part is invalid. + * + * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} + * @param index - index before the part + * + * @return the part of text before that index, or null */ public String getBeforeIndex(int part, int index) throws NotImplementedException @@ -368,10 +380,10 @@ public abstract class JTextComponent extends JComponent } /** - * Get the number possible actions for this object, with the zeroth - * representing the default action. + * Returns the number of actions for this object. The zero-th + * object represents the default action. * - * @return the 0-based number of actions + * @return the number of actions (0-based). */ public int getAccessibleActionCount() throws NotImplementedException @@ -380,11 +392,12 @@ public abstract class JTextComponent extends JComponent } /** - * Get a description for the specified action. Returns null if out of - * bounds. + * Returns the description of the i-th action. Null is returned if + * i is out of bounds. * - * @param i the action to describe, 0-based - * @return description of the action + * @param i - the action to get the description for + * + * @return description of the i-th action */ public String getAccessibleActionDescription(int i) throws NotImplementedException @@ -394,10 +407,12 @@ public abstract class JTextComponent extends JComponent } /** - * Perform the specified action. Does nothing if out of bounds. + * Performs the i-th action. Nothing happens if i is + * out of bounds. * - * @param i the action to perform, 0-based - * @return true if the action was performed + * @param i - the action to perform + * + * @return true if the action was performed successfully */ public boolean doAccessibleAction(int i) throws NotImplementedException @@ -406,9 +421,9 @@ public abstract class JTextComponent extends JComponent } /** - * Set the text contents to the given string. + * Sets the text contents. * - * @param s the new text + * @param s - the new text contents. */ public void setTextContents(String s) throws NotImplementedException @@ -417,10 +432,10 @@ public abstract class JTextComponent extends JComponent } /** - * Inserts the given string at the specified location. + * Inserts the text at the given index. * - * @param index the index for insertion - * @param s the new text + * @param index - the index to insert the new text at. + * @param s - the new text */ public void insertTextAtIndex(int index, String s) throws NotImplementedException @@ -429,10 +444,10 @@ public abstract class JTextComponent extends JComponent } /** - * Return the text between two points. + * Gets the text between two indexes. * - * @param start the start position, inclusive - * @param end the end position, exclusive + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) */ public String getTextRange(int start, int end) { @@ -447,10 +462,10 @@ public abstract class JTextComponent extends JComponent } /** - * Delete the text between two points. + * Deletes the text between two indexes. * - * @param start the start position, inclusive - * @param end the end position, exclusive + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) */ public void delete(int start, int end) { @@ -458,10 +473,11 @@ public abstract class JTextComponent extends JComponent } /** - * Cut the text between two points to the system clipboard. + * Cuts the text between two indexes. The text is put + * into the system clipboard. * - * @param start the start position, inclusive - * @param end the end position, exclusive + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) */ public void cut(int start, int end) { @@ -470,9 +486,9 @@ public abstract class JTextComponent extends JComponent } /** - * Paste the text from the system clipboard at the given index. + * Pastes the text from the system clipboard to the given index. * - * @param start the start position + * @param start - the starting index */ public void paste(int start) { @@ -481,11 +497,12 @@ public abstract class JTextComponent extends JComponent } /** - * Replace the text between two points with the given string. + * Replaces the text between two indexes with the given text. * - * @param start the start position, inclusive - * @param end the end position, exclusive - * @param s the string to paste + * + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) + * @param s - the text to paste */ public void replaceText(int start, int end, String s) { @@ -494,10 +511,10 @@ public abstract class JTextComponent extends JComponent } /** - * Select the text between two points. + * Selects the text between two indexes. * - * @param start the start position, inclusive - * @param end the end position, exclusive + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) */ public void selectText(int start, int end) { @@ -505,11 +522,11 @@ public abstract class JTextComponent extends JComponent } /** - * Set the attributes of text between two points. + * Sets the attributes of all the text between two indexes. * - * @param start the start position, inclusive - * @param end the end position, exclusive - * @param s the new attribute set for the range + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) + * @param s - the new attribute set for the text in the range */ public void setAttributes(int start, int end, AttributeSet s) throws NotImplementedException @@ -1163,8 +1180,19 @@ public abstract class JTextComponent extends JComponent public void setDocument(Document newDoc) { Document oldDoc = doc; - doc = newDoc; - firePropertyChange("document", oldDoc, newDoc); + try + { + if (oldDoc instanceof AbstractDocument) + ((AbstractDocument) oldDoc).readLock(); + + doc = newDoc; + firePropertyChange("document", oldDoc, newDoc); + } + finally + { + if (oldDoc instanceof AbstractDocument) + ((AbstractDocument) oldDoc).readUnlock(); + } revalidate(); repaint(); } @@ -1641,10 +1669,12 @@ public abstract class JTextComponent extends JComponent public boolean getScrollableTracksViewportWidth() { - if (getParent() instanceof JViewport) - return getParent().getWidth() > getPreferredSize().width; + boolean res = false;; + Container c = getParent(); + if (c instanceof JViewport) + res = ((JViewport) c).getExtentSize().width > getPreferredSize().width; - return false; + return res; } /** diff --git a/libjava/classpath/javax/swing/text/ParagraphView.java b/libjava/classpath/javax/swing/text/ParagraphView.java index 15bed781825..c4857863d35 100644 --- a/libjava/classpath/javax/swing/text/ParagraphView.java +++ b/libjava/classpath/javax/swing/text/ParagraphView.java @@ -74,6 +74,39 @@ public class ParagraphView extends FlowView implements TabExpander return align; } + /** + * Allows rows to span the whole parent view. + */ + public float getMaximumSpan(int axis) + { + float max; + if (axis == X_AXIS) + max = Float.MAX_VALUE; + else + max = super.getMaximumSpan(axis); + return max; + } + + /** + * Overridden because child views are not necessarily laid out in model + * order. + */ + protected int getViewIndexAtPosition(int pos) + { + int index = -1; + if (pos >= getStartOffset() && pos < getEndOffset()) + { + int nviews = getViewCount(); + for (int i = 0; i < nviews && index == -1; i++) + { + View child = getView(i); + if (pos >= child.getStartOffset() && pos < child.getEndOffset()) + index = i; + } + } + return index; + } + protected void loadChildren(ViewFactory vf) { // Do nothing here. The children are added while layouting. @@ -140,7 +173,7 @@ public class ParagraphView extends FlowView implements TabExpander { float align; if (axis == X_AXIS) - align = super.getAlignment(axis); + align = 0.5F; else if (getViewCount() > 0) { float prefHeight = getPreferredSpan(Y_AXIS); @@ -148,7 +181,7 @@ public class ParagraphView extends FlowView implements TabExpander align = (firstRowHeight / 2.F) / prefHeight; } else - align = 0.0F; + align = 0.5F; return align; } diff --git a/libjava/classpath/javax/swing/text/PlainDocument.java b/libjava/classpath/javax/swing/text/PlainDocument.java index c699dcad2aa..730a619da9f 100644 --- a/libjava/classpath/javax/swing/text/PlainDocument.java +++ b/libjava/classpath/javax/swing/text/PlainDocument.java @@ -56,8 +56,12 @@ public class PlainDocument extends AbstractDocument public static final String lineLimitAttribute = "lineLimit"; public static final String tabSizeAttribute = "tabSize"; - private BranchElement rootElement; - private int tabSize; + /** + * The default root element of this document. This is made type Element + * because the RI seems to accept other types of elements as well from + * createDefaultRoot() (when overridden by a subclass). + */ + private Element rootElement; public PlainDocument() { @@ -67,8 +71,10 @@ public class PlainDocument extends AbstractDocument public PlainDocument(AbstractDocument.Content content) { super(content); - tabSize = 8; - rootElement = (BranchElement) createDefaultRoot(); + rootElement = createDefaultRoot(); + + // This property has been determined using a Mauve test. + putProperty("tabSize", new Integer(8)); } private void reindex() @@ -105,10 +111,10 @@ public class PlainDocument extends AbstractDocument protected AbstractDocument.AbstractElement createDefaultRoot() { BranchElement root = - (BranchElement) createBranchElement(null, SimpleAttributeSet.EMPTY); + (BranchElement) createBranchElement(null, null); Element[] array = new Element[1]; - array[0] = createLeafElement(root, SimpleAttributeSet.EMPTY, 0, 1); + array[0] = createLeafElement(root, null, 0, 1); root.replace(0, 0, array); return root; @@ -117,116 +123,97 @@ public class PlainDocument extends AbstractDocument protected void insertUpdate(DefaultDocumentEvent event, AttributeSet attributes) { + + String text = null; int offset = event.getOffset(); - int eventLength = event.getLength(); - int end = offset + event.getLength(); - int oldElementIndex, elementIndex = rootElement.getElementIndex(offset); - Element firstElement = rootElement.getElement(elementIndex); - oldElementIndex = elementIndex; - - // If we're inserting immediately after a newline we have to fix the - // Element structure (but only if we are dealing with a line which - // has not existed as Element before). - if (offset > 0 && firstElement.getStartOffset() != offset) + int length = event.getLength(); + try { - try - { - String s = getText(offset - 1, 1); - if (s.equals("\n") ) - { - int newEl2EndOffset = end; - boolean replaceNext = false; - if (rootElement.getElementCount() > elementIndex + 1) - { - replaceNext = true; - newEl2EndOffset = - rootElement.getElement(elementIndex + 1).getEndOffset(); - } - Element newEl1 = - createLeafElement(rootElement, firstElement.getAttributes(), - firstElement.getStartOffset(), offset); - Element newEl2 = - createLeafElement (rootElement, firstElement.getAttributes(), - offset, newEl2EndOffset); - if (replaceNext) - rootElement.replace(elementIndex, 2, new Element[] { newEl1, newEl2 }); - else - rootElement.replace(elementIndex, 1, new Element[] { newEl1, newEl2 }); - firstElement = newEl2; - elementIndex ++; - } - } - catch (BadLocationException ble) - { - // This shouldn't happen. - AssertionError ae = new AssertionError(); - ae.initCause(ble); - throw ae; - } + text = getText(offset, length); + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError(); + err.initCause(ex); + throw err; } - // added and removed are Element arrays used to add an ElementEdit - // to the DocumentEvent if there were entire lines added or removed. - Element[] removed = new Element[1]; - Element[] added; - try + boolean hasLineBreak = text.indexOf('\n') != -1; + boolean prevCharIsLineBreak = false; + try { - String str = content.getString(offset, eventLength); - ArrayList elts = new ArrayList(); + prevCharIsLineBreak = + offset > 0 && getText(offset - 1, 1).charAt(0) == '\n'; + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError(); + err.initCause(ex); + throw err; + } + boolean lastCharIsLineBreak = text.charAt(text.length() - 1) == '\n'; + int lineIndex = -1; + int lineStart = -1; + int lineEnd = -1; + Element[] removed = null; + BranchElement root = (BranchElement) rootElement; + boolean updateStructure = true; - // Determine how many NEW lines were added by finding the newline - // characters within the newly inserted text - int j = firstElement.getStartOffset(); - int i = str.indexOf('\n', 0); - int contentLength = content.length(); - - while (i != -1 && i <= eventLength) - { - // For each new line, create a new element - elts.add(createLeafElement(rootElement, SimpleAttributeSet.EMPTY, - j, offset + i + 1)); - - j = offset + i + 1; - if (j >= contentLength) - break; - i = str.indexOf('\n', i + 1); - } + if (prevCharIsLineBreak && ! lastCharIsLineBreak) + { + // We must fix the structure a little if the previous char + // is a linebreak and the last char isn't. + lineIndex = root.getElementIndex(offset - 1); + Element prevLine = root.getElement(lineIndex); + Element nextLine = root.getElement(lineIndex + 1); + lineStart = prevLine.getStartOffset(); + lineEnd = nextLine.getEndOffset(); + removed = new Element[]{ prevLine, nextLine }; + } + else if (hasLineBreak) + { + lineIndex = root.getElementIndex(offset); + Element line = root.getElement(lineIndex); + lineStart = line.getStartOffset(); + lineEnd = line.getEndOffset(); + removed = new Element[]{ line }; + } + else + { + updateStructure = false; + } - // If there were new lines added we have to add an ElementEdit to - // the DocumentEvent and we have to call rootElement.replace to - // insert the new lines - if (elts.size() != 0) + if (updateStructure) + { + // Break the lines between lineStart and lineEnd. + ArrayList lines = new ArrayList(); + int len = lineEnd - lineStart; + try { - // If we have created new lines test whether there are remaining - // characters in firstElement after the inserted text and if so - // create a new element for them. - if (j < firstElement.getEndOffset()) - elts.add(createLeafElement(rootElement, SimpleAttributeSet.EMPTY, j, firstElement.getEndOffset())); - - // Set up the ElementEdit by filling the added and removed - // arrays with the proper Elements - added = new Element[elts.size()]; - elts.toArray(added); - - removed[0] = firstElement; - - // Now create and add the ElementEdit - ElementEdit e = new ElementEdit(rootElement, elementIndex, removed, - added); - event.addEdit(e); - - // And call replace to actually make the changes - ((BranchElement) rootElement).replace(elementIndex, 1, added); + text = getText(lineStart, len); } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError(); + err.initCause(ex); + throw err; + } + int prevLineBreak = 0; + int lineBreak = text.indexOf('\n'); + do + { + lineBreak++; + lines.add(createLeafElement(root, null, lineStart + prevLineBreak, + lineStart + lineBreak)); + prevLineBreak = lineBreak; + lineBreak = text.indexOf('\n', prevLineBreak); + } while (prevLineBreak < len); + + // Update the element structure and prepare document event. + Element[] added = (Element[]) lines.toArray(new Element[lines.size()]); + event.addEdit(new ElementEdit(root, lineIndex, removed, added)); + root.replace(lineIndex, removed.length, added); } - catch (BadLocationException e) - { - // This shouldn't happen so we throw an AssertionError - AssertionError ae = new AssertionError(); - ae.initCause(e); - throw ae; - } - super.insertUpdate(event, attributes); } @@ -264,7 +251,7 @@ public class PlainDocument extends AbstractDocument event.addEdit(e); // collapse elements if the removal spans more than 1 line - rootElement.replace(i1, i2 - i1 + 1, added); + ((BranchElement) rootElement).replace(i1, i2 - i1 + 1, added); } } diff --git a/libjava/classpath/javax/swing/text/Segment.java b/libjava/classpath/javax/swing/text/Segment.java index d2364e05a10..63c5fa09dbc 100644 --- a/libjava/classpath/javax/swing/text/Segment.java +++ b/libjava/classpath/javax/swing/text/Segment.java @@ -165,8 +165,9 @@ public class Segment implements Cloneable, CharacterIterator /** * Sets the current index to point to the last character in the segment and - * returns that character. If the segment contains zero characters, this - * method returns {@link #DONE}. + * returns that character. If the segment contains zero characters, the + * current index is set to {@link #getEndIndex()} and this method returns + * {@link #DONE}. * * @return The last character in the segment, or {@link #DONE} if the * segment contains zero characters. @@ -174,7 +175,10 @@ public class Segment implements Cloneable, CharacterIterator public char last() { if (count == 0) - return DONE; + { + current = getEndIndex(); + return DONE; + } current = getEndIndex() - 1; return array[current]; diff --git a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java index 8dbcb0c6a14..8684ef87d34 100644 --- a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java +++ b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java @@ -51,8 +51,10 @@ public class SimpleAttributeSet /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = 8267656273837665219L; - /** An empty attribute set. */ - public static final AttributeSet EMPTY = new SimpleAttributeSet(); + /** + * An empty attribute set. + */ + public static final AttributeSet EMPTY = new EmptyAttributeSet(); /** Storage for the attributes. */ Hashtable tab; diff --git a/libjava/classpath/javax/swing/text/StringContent.java b/libjava/classpath/javax/swing/text/StringContent.java index 0a31505f3a6..8014dc3bce6 100644 --- a/libjava/classpath/javax/swing/text/StringContent.java +++ b/libjava/classpath/javax/swing/text/StringContent.java @@ -178,11 +178,13 @@ public final class StringContent } /** - * Creates a new instance containing the string "\n". + * Creates a new instance containing the string "\n". This is equivalent + * to calling {@link #StringContent(int)} with an <code>initialLength</code> + * of 10. */ public StringContent() { - this(1); + this(10); } /** diff --git a/libjava/classpath/javax/swing/text/TabSet.java b/libjava/classpath/javax/swing/text/TabSet.java index ecad9444ea5..0f2c8c7c1ee 100644 --- a/libjava/classpath/javax/swing/text/TabSet.java +++ b/libjava/classpath/javax/swing/text/TabSet.java @@ -1,5 +1,5 @@ /* TabSet.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,23 +39,54 @@ package javax.swing.text; import java.io.Serializable; +/** + * A set of tab stops. Instances of this class are immutable. + */ public class TabSet implements Serializable { /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = 2367703481999080593L; + /** Storage for the tab stops. */ TabStop[] tabs; + /** + * Creates a new <code>TabSet</code> containing the specified tab stops. + * + * @param t the tab stops (<code>null</code> permitted). + */ public TabSet(TabStop[] t) { - tabs = t; + if (t != null) + tabs = (TabStop[]) t.clone(); + else + tabs = new TabStop[0]; } + /** + * Returns the tab stop with the specified index. + * + * @param i the index. + * + * @return The tab stop. + * + * @throws IllegalArgumentException if <code>i</code> is not in the range + * <code>0</code> to <code>getTabCount() - 1</code>. + */ public TabStop getTab(int i) { + if (i < 0 || i >= tabs.length) + throw new IllegalArgumentException("Index out of bounds."); return tabs[i]; } + /** + * Returns the tab following the specified location. + * + * @param location the location. + * + * @return The tab following the specified location (or <code>null</code>). + */ public TabStop getTabAfter(float location) { int idx = getTabIndexAfter(location); @@ -65,11 +96,23 @@ public class TabSet implements Serializable return tabs[idx]; } + /** + * Returns the number of tab stops in this tab set. + * + * @return The number of tab stops in this tab set. + */ public int getTabCount() { return tabs.length; } + /** + * Returns the index of the specified tab, or -1 if the tab is not found. + * + * @param tab the tab (<code>null</code> permitted). + * + * @return The index of the specified tab, or -1. + */ public int getTabIndex(TabStop tab) { for (int i = 0; i < tabs.length; ++i) @@ -78,28 +121,88 @@ public class TabSet implements Serializable return -1; } + /** + * Returns the index of the tab at or after the specified location. + * + * @param location the tab location. + * + * @return The index of the tab stop, or -1. + */ public int getTabIndexAfter(float location) { - int idx = -1; - for (int i = 0; i < tabs.length; ++i) + for (int i = 0; i < tabs.length; i++) + { + if (location <= tabs[i].getPosition()) + return i; + } + return -1; + } + + /** + * Tests this <code>TabSet</code> for equality with an arbitrary object. + * + * @param obj the object (<code>null</code> permitted). + * + * @return <code>true</code> if this <code>TabSet</code> is equal to + * <code>obj</code>, and <code>false</code> otherwise. + * + * @since 1.5 + */ + public boolean equals(Object obj) + { + if (obj == this) + return true; + if (!(obj instanceof TabSet)) + return false; + TabSet that = (TabSet) obj; + int tabCount = getTabCount(); + if (tabCount != that.getTabCount()) + return false; + for (int i = 0; i < tabCount; i++) + { + if (!this.getTab(i).equals(that.getTab(i))) + return false; + } + return true; + } + + /** + * Returns a hash code for this <code>TabSet</code>. + * + * @return A hash code. + * + * @since 1.5 + */ + public int hashCode() + { + // this hash code won't match Sun's, but that shouldn't matter... + int result = 193; + int tabs = getTabCount(); + for (int i = 0; i < tabs; i++) { - if (location < tabs[i].getPosition()) - idx = i; + TabStop t = getTab(i); + if (t != null) + result = 37 * result + t.hashCode(); } - return idx; + return result; } + /** + * Returns a string representation of this <code>TabSet</code>. + * + * @return A string representation of this <code>TabSet</code>. + */ public String toString() { StringBuffer sb = new StringBuffer(); - sb.append("["); + sb.append("[ "); for (int i = 0; i < tabs.length; ++i) { if (i != 0) sb.append(" - "); sb.append(tabs[i].toString()); } - sb.append("]"); + sb.append(" ]"); return sb.toString(); } } diff --git a/libjava/classpath/javax/swing/text/TabStop.java b/libjava/classpath/javax/swing/text/TabStop.java index 56f862fdae4..f4c3f851406 100644 --- a/libjava/classpath/javax/swing/text/TabStop.java +++ b/libjava/classpath/javax/swing/text/TabStop.java @@ -1,5 +1,5 @@ -/* TabSet.java -- - Copyright (C) 2004 Free Software Foundation, Inc. +/* TabStop.java -- + Copyright (C) 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,9 @@ package javax.swing.text; import java.io.Serializable; +/** + * Represents a tab position in some text. + */ public class TabStop implements Serializable { /** The serialization UID (compatible with JDK1.5). */ @@ -61,18 +64,42 @@ public class TabStop implements Serializable int align; int leader; + /** + * Creates a new <code>TabStop</code> for the specified tab position. + * + * @param pos the tab position. + */ public TabStop(float pos) { this(pos, ALIGN_LEFT, LEAD_NONE); } + /** + * Creates a new <code>TabStop</code> with the specified attributes. + * + * @param pos the tab position. + * @param align the alignment (one of {@link #ALIGN_LEFT}, + * {@link #ALIGN_CENTER}, {@link #ALIGN_RIGHT}, {@link #ALIGN_DECIMAL} + * or {@link #ALIGN_BAR}). + * @param leader the leader (one of {@link #LEAD_NONE}, {@link #LEAD_DOTS}, + * {@link #LEAD_EQUALS}, {@link #LEAD_HYPHENS}, {@link #LEAD_THICKLINE} + * or {@link #LEAD_UNDERLINE}). + */ public TabStop(float pos, int align, int leader) { this.pos = pos; this.align = align; this.leader = leader; } - + + /** + * Tests this <code>TabStop</code> for equality with an arbitrary object. + * + * @param other the other object (<code>null</code> permitted). + * + * @return <code>true</code> if this <code>TabStop</code> is equal to + * the specified object, and <code>false</code> otherwise. + */ public boolean equals(Object other) { return (other != null) @@ -82,34 +109,60 @@ public class TabStop implements Serializable && (((TabStop)other).getAlignment() == this.getAlignment()); } + /** + * Returns the tab alignment. This should be one of {@link #ALIGN_LEFT}, + * {@link #ALIGN_CENTER}, {@link #ALIGN_RIGHT}, {@link #ALIGN_DECIMAL} or + * {@link #ALIGN_BAR}. + * + * @return The tab alignment. + */ public int getAlignment() { return align; } + /** + * Returns the leader type. This should be one of {@link #LEAD_NONE}, + * {@link #LEAD_DOTS}, {@link #LEAD_EQUALS}, {@link #LEAD_HYPHENS}, + * {@link #LEAD_THICKLINE} or {@link #LEAD_UNDERLINE}. + * + * @return The leader type. + */ public int getLeader() { return leader; } + /** + * Returns the tab position. + * + * @return The tab position. + */ public float getPosition() { return pos; } + /** + * Returns a hash code for this <code>TabStop</code>. + * + * @return A hash code. + */ public int hashCode() { return (int) pos + (int) leader + (int) align; } + /** + * Returns a string describing this <code>TabStop</code>. + * + * @return A string describing this <code>TabStop</code>. + */ public String toString() { String prefix = ""; switch (align) { - case ALIGN_LEFT: - prefix = "left "; - break; case ALIGN_RIGHT: prefix = "right "; break; @@ -130,7 +183,8 @@ public class TabStop implements Serializable break; } - return (prefix + "tab @" + pos + ((leader == LEAD_NONE) ? "" : "(w/leaders)")); + return prefix + "tab @" + pos + + ((leader == LEAD_NONE) ? "" : " (w/leaders)"); } } diff --git a/libjava/classpath/javax/swing/text/View.java b/libjava/classpath/javax/swing/text/View.java index d8ad5f5858e..55a63f6b668 100644 --- a/libjava/classpath/javax/swing/text/View.java +++ b/libjava/classpath/javax/swing/text/View.java @@ -401,7 +401,10 @@ public abstract class View implements SwingConstants Element el = getElement(); DocumentEvent.ElementChange ec = ev.getChange(el); if (ec != null) - updateChildren(ec, ev, vf); + { + if (! updateChildren(ec, ev, vf)) + ec = null; + } forwardUpdate(ec, ev, shape, vf); updateLayout(ec, ev, shape); } @@ -493,27 +496,66 @@ public abstract class View implements SwingConstants int count = getViewCount(); if (count > 0) { + // Determine start index. int startOffset = ev.getOffset(); - int endOffset = startOffset + ev.getLength(); int startIndex = getViewIndex(startOffset, Position.Bias.Backward); - int endIndex = getViewIndex(endOffset, Position.Bias.Forward); - int index = -1; - int addLength = -1; - if (ec != null) + + // For REMOVE events we have to forward the event to the last element, + // for the case that an Element has been removed that represente + // the offset. + if (startIndex == -1 && ev.getType() == DocumentEvent.EventType.REMOVE + && startOffset >= getEndOffset()) { - index = ec.getIndex(); - addLength = ec.getChildrenAdded().length; + startIndex = getViewCount() - 1; } - if (startIndex >= 0 && endIndex >= 0) + // When startIndex is on a view boundary, forward event to the + // previous view too. + if (startIndex >= 0) { - for (int i = startIndex; i <= endIndex; i++) + View v = getView(startIndex); + if (v != null) + { + if (v.getStartOffset() == startOffset && startOffset > 0) + startIndex = Math.max(0, startIndex - 1); + } + } + startIndex = Math.max(0, startIndex); + + // Determine end index. + int endIndex = startIndex; + if (ev.getType() != DocumentEvent.EventType.REMOVE) + { + endIndex = getViewIndex(startOffset + ev.getLength(), + Position.Bias.Forward); + if (endIndex < 0) + endIndex = getViewCount() - 1; + } + + // Determine hole that comes from added elements (we don't forward + // the event to newly added views. + int startAdded = endIndex + 1; + int endAdded = startAdded; + Element[] added = (ec != null) ? ec.getChildrenAdded() : null; + if (added != null && added.length > 0) + { + startAdded = ec.getIndex(); + endAdded = startAdded + added.length - 1; + } + + // Forward event to all views between startIndex and endIndex, + // and leave out all views in the hole. + for (int i = startIndex; i <= endIndex; i++) + { + // Skip newly added child views. + if (! (i >= startAdded && i <= endAdded)) { - // Skip newly added child views. - if (index >= 0 && i >= index && i < (index+addLength)) - continue; View child = getView(i); - forwardUpdateToView(child, ev, shape, vf); + if (child != null) + { + Shape childAlloc = getChildAllocation(i, shape); + forwardUpdateToView(child, ev, childAlloc, vf); + } } } } @@ -611,9 +653,46 @@ public abstract class View implements SwingConstants if (b2 != Position.Bias.Forward && b2 != Position.Bias.Backward) throw new IllegalArgumentException ("b2 must be either Position.Bias.Forward or Position.Bias.Backward"); - Rectangle s1 = (Rectangle) modelToView(p1, a, b1); - Rectangle s2 = (Rectangle) modelToView(p2, a, b2); - return SwingUtilities.computeUnion(s1.x, s1.y, s1.width, s1.height, s2); + + Shape s1 = modelToView(p1, a, b1); + // Special case for p2 == end index. + Shape s2; + if (p2 != getEndOffset()) + { + s2 = modelToView(p2, a, b2); + } + else + { + try + { + s2 = modelToView(p2, a, b2); + } + catch (BadLocationException ex) + { + // Assume the end rectangle to be at the right edge of the + // view. + Rectangle aRect = a instanceof Rectangle ? (Rectangle) a + : a.getBounds(); + s2 = new Rectangle(aRect.x + aRect.width - 1, aRect.y, 1, + aRect.height); + } + } + + // Need to modify the rectangle, so we create a copy in all cases. + Rectangle r1 = s1.getBounds(); + Rectangle r2 = s2 instanceof Rectangle ? (Rectangle) s2 + : s2.getBounds(); + + // For multiline view, let the resulting rectangle span the whole view. + if (r1.y != r2.y) + { + Rectangle aRect = a instanceof Rectangle ? (Rectangle) a + : a.getBounds(); + r1.x = aRect.x; + r1.width = aRect.width; + } + + return SwingUtilities.computeUnion(r2.x, r2.y, r2.width, r2.height, r1); } /** diff --git a/libjava/classpath/javax/swing/text/html/HTMLTableView.java b/libjava/classpath/javax/swing/text/html/BRView.java index cac44d8dc27..5521fed8edf 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLTableView.java +++ b/libjava/classpath/javax/swing/text/html/BRView.java @@ -1,4 +1,4 @@ -/* HTMLTableView.java -- A table view for HTML tables +/* BRView.java -- HTML BR tag view Copyright (C) 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,44 +39,33 @@ exception statement from your version. */ package javax.swing.text.html; import javax.swing.text.Element; -import javax.swing.text.TableView; -import javax.swing.text.View; -import javax.swing.text.ViewFactory; /** - * A conrete implementation of TableView that renders HTML tables. - * - * @author Roman Kennke (kennke@aicas.com) + * Handled the HTML BR tag. */ -class HTMLTableView - extends TableView -{ +class BRView + extends NullView +{ /** - * Creates a new HTMLTableView for the specified element. - * - * @param el the element for the table view + * Creates the new BR view. + * + * @param elem the HTML element, representing the view. */ - public HTMLTableView(Element el) + public BRView(Element elem) { - super(el); + super(elem); } - + /** - * Loads the children of the Table. This completely bypasses the ViewFactory - * and creates instances of TableRow instead. - * - * @param vf ignored + * Always return ForcedBreakWeight for the X_AXIS, BadBreakWeight for the + * Y_AXIS. */ - protected void loadChildren(ViewFactory vf) + public int getBreakWeight(int axis, float pos, float len) { - Element el = getElement(); - int numChildren = el.getElementCount(); - View[] rows = new View[numChildren]; - for (int i = 0; i < numChildren; ++i) - { - rows[i] = createTableRow(el.getElement(i)); - } - replace(0, getViewCount(), rows); + if (axis == X_AXIS) + return ForcedBreakWeight; + else + return BadBreakWeight; } } diff --git a/libjava/classpath/javax/swing/text/html/HRuleView.java b/libjava/classpath/javax/swing/text/html/HRuleView.java new file mode 100644 index 00000000000..3bae5eb8e83 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/HRuleView.java @@ -0,0 +1,189 @@ +/* HRuleView.java -- Horizontal dash in HTML documents. + 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 javax.swing.text.html; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.text.Element; +import javax.swing.text.View; + +/** + * Represents the long horizontal separating dash that can be inserted into the + * HTML documents with HR tag. + */ +class HRuleView extends InlineView +{ + /** + * The null view, indicating, that nothing should be painted ahead the + * breaking point. + */ + View nullView; + + /** + * The height of the horizontal dash area. + */ + static int HEIGHT = 4; + + /** + * The imaginary invisible view that stays after end of line after the + * breaking procedure. It occupies on character. + */ + class Beginning extends NullView + { + /** + * The break offset that becomes the views start offset. + */ + int breakOffset; + + /** + * Return the end offset that is always one char after the break offset. + */ + public int getEndOffset() + { + return breakOffset + 1; + } + + /** + * Return the start offset that has been passed in a constructor. + */ + public int getStartOffset() + { + return breakOffset; + } + + /** + * Create the new instance of this view. + * + * @param element the element (inherited from the HR view) + * @param offset the position where the HR view has been broken + */ + public Beginning(Element element, int offset) + { + super(element); + breakOffset = offset; + } + } + + /** + * Creates the new HR view. + */ + public HRuleView(Element element) + { + super(element); + } + + /** + * Returns the ForcedBreakWeight for the vertical axis, indicating, the the + * view must be broken to be displayed correctly. The horizontal dash is + * not breakeable along the Y axis. + */ + public int getBreakWeight(int axis, float pos, float len) + { + if (axis == X_AXIS && ((getEndOffset() - getStartOffset()) > 1)) + return ForcedBreakWeight; + else + return BadBreakWeight; + } + + /** + * Draws the double line, upped black and the lower light gray. + */ + public void paint(Graphics g, Shape a) + { + Rectangle bounds = a.getBounds(); + + int x = bounds.x; + int y = bounds.y; + + int w = bounds.x + bounds.width; + + // We move "half pixel up" from the actual horizontal position - + // this will be rounded to the closest actual int co-ordinate. + int h = bounds.y + (int) Math.round(bounds.height * 0.5 - 0.5); + + g.setColor(Color.black); + g.drawLine(x, y++, w, h++); + g.setColor(Color.lightGray); + g.drawLine(x, y, w, h); + } + + /** + * Break the view into this view and the invisible imaginary view that + * stays on the end of line that is broken by HR dash. The view is broken + * only if its length is longer than one (the two characters are expected + * in the initial length). + */ + public View breakView(int axis, int offset, float pos, float len) + { + if (getEndOffset() - getStartOffset() > 1) + return new Beginning(getElement(), offset); + else + return this; + } + + /** + * Returns the width of the container for the horizontal axis and the + * thickness of the dash area for the vertical axis. + */ + public float getMaximumSpan(int axis) + { + if (axis == X_AXIS) + { + Component container = getContainer(); + if (container != null) + return getContainer().getWidth(); + else + return 640; + } + else + return HEIGHT; + } + + /** + * Returns the same values as {@link #getMaximumSpan(int)} + */ + public float getPreferredSpan(int axis) + { + return getMaximumSpan(axis); + } +} diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java index e714a857b61..0bfc338df45 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLDocument.java +++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java @@ -40,14 +40,18 @@ package javax.swing.text.html; import gnu.classpath.NotImplementedException; import gnu.javax.swing.text.html.CharacterAttributeTranslator; +import gnu.javax.swing.text.html.parser.htmlAttributeSet; import java.io.IOException; +import java.io.StringReader; import java.net.URL; import java.util.HashMap; import java.util.Stack; import java.util.Vector; import javax.swing.JEditorPane; +import javax.swing.event.DocumentEvent; +import javax.swing.event.HyperlinkEvent.EventType; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; @@ -515,19 +519,23 @@ public class HTMLDocument extends DefaultStyledDocument */ public class HTMLReader extends HTMLEditorKit.ParserCallback { - /** Holds the current character attribute set **/ + /** + * Holds the current character attribute set * + */ protected MutableAttributeSet charAttr = new SimpleAttributeSet(); protected Vector parseBuffer = new Vector(); - /** A stack for character attribute sets **/ + /** + * A stack for character attribute sets * + */ Stack charAttrStack = new Stack(); /** * The parse stack. This stack holds HTML.Tag objects that reflect the * current position in the parsing process. */ - private Stack parseStack = new Stack(); + Stack parseStack = new Stack(); /** A mapping between HTML.Tag objects and the actions that handle them **/ HashMap tagToAction; @@ -535,10 +543,31 @@ public class HTMLDocument extends DefaultStyledDocument /** Tells us whether we've received the '</html>' tag yet **/ boolean endHTMLEncountered = false; - /** Variables related to the constructor with explicit insertTag **/ - int popDepth, pushDepth, offset; + /** + * Related to the constructor with explicit insertTag + */ + int popDepth; + + /** + * Related to the constructor with explicit insertTag + */ + int pushDepth; + + /** + * Related to the constructor with explicit insertTag + */ + int offset; + + /** + * The tag (inclusve), after that the insertion should start. + */ HTML.Tag insertTag; - boolean insertTagEncountered = false; + + /** + * This variable becomes true after the insert tag has been encountered. + */ + boolean insertTagEncountered; + /** A temporary variable that helps with the printing out of debug information **/ boolean debug = false; @@ -1139,8 +1168,21 @@ public class HTMLDocument extends DefaultStyledDocument } /** - * This method is called by the parser and should route the call to - * the proper handler for the tag. + * Checks if the HTML tag should be inserted. The tags before insert tag (if + * specified) are not inserted. Also, the tags after the end of the html are + * not inserted. + * + * @return true if the tag should be inserted, false otherwise. + */ + private boolean shouldInsert() + { + return ! endHTMLEncountered + && (insertTagEncountered || insertTag == null); + } + + /** + * This method is called by the parser and should route the call to the + * proper handler for the tag. * * @param t the HTML.Tag * @param a the attribute set @@ -1148,13 +1190,15 @@ public class HTMLDocument extends DefaultStyledDocument */ public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) { - // Don't call the Action if we've already seen </html>. - if (endHTMLEncountered) - return; - - TagAction action = (TagAction) tagToAction.get(t); - if (action != null) - action.start(t, a); + if (t == insertTag) + insertTagEncountered = true; + + if (shouldInsert()) + { + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + action.start(t, a); + } } /** @@ -1165,42 +1209,41 @@ public class HTMLDocument extends DefaultStyledDocument */ public void handleComment(char[] data, int pos) { - // Don't call the Action if we've already seen </html>. - if (endHTMLEncountered) - return; - - TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT); - if (action != null) + if (shouldInsert()) { - action.start(HTML.Tag.COMMENT, new SimpleAttributeSet()); - action.end (HTML.Tag.COMMENT); + TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT); + if (action != null) + { + action.start(HTML.Tag.COMMENT, + htmlAttributeSet.EMPTY_HTML_ATTRIBUTE_SET); + action.end(HTML.Tag.COMMENT); + } } } /** - * This method is called by the parser and should route the call to - * the proper handler for the tag. + * This method is called by the parser and should route the call to the + * proper handler for the tag. * * @param t the HTML.Tag * @param pos the position at which the tag was encountered */ public void handleEndTag(HTML.Tag t, int pos) { - // Don't call the Action if we've already seen </html>. - if (endHTMLEncountered) - return; - - // If this is the </html> tag we need to stop calling the Actions - if (t == HTML.Tag.HTML) - endHTMLEncountered = true; - - TagAction action = (TagAction) tagToAction.get(t); - if (action != null) - action.end(t); + if (shouldInsert()) + { + // If this is the </html> tag we need to stop calling the Actions + if (t == HTML.Tag.HTML) + endHTMLEncountered = true; + + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + action.end(t); + } } /** - * This is a callback from the parser that should be routed to the + * This is a callback from the parser that should be routed to the * appropriate handler for the tag. * * @param t the HTML.Tag that was encountered @@ -1209,15 +1252,17 @@ public class HTMLDocument extends DefaultStyledDocument */ public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) { - // Don't call the Action if we've already seen </html>. - if (endHTMLEncountered) - return; - - TagAction action = (TagAction) tagToAction.get (t); - if (action != null) + if (t == insertTag) + insertTagEncountered = true; + + if (shouldInsert()) { - action.start(t, a); - action.end(t); + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + { + action.start(t, a); + action.end(t); + } } } @@ -1230,7 +1275,6 @@ public class HTMLDocument extends DefaultStyledDocument * @since 1.3 */ public void handleEndOfLineString(String eol) - throws NotImplementedException { // FIXME: Implement. print ("HTMLReader.handleEndOfLineString not implemented yet"); @@ -1273,16 +1317,6 @@ public class HTMLDocument extends DefaultStyledDocument printBuffer(); DefaultStyledDocument.ElementSpec element; - // If the previous tag is content and the parent is p-implied, then - // we must also close the p-implied. - if (parseStack.size() > 0 && parseStack.peek() == HTML.Tag.IMPLIED) - { - element = new DefaultStyledDocument.ElementSpec(null, - DefaultStyledDocument.ElementSpec.EndTagType); - parseBuffer.addElement(element); - parseStack.pop(); - } - parseStack.push(t); AbstractDocument.AttributeContext ctx = getAttributeContext(); AttributeSet copy = attr.copyAttributes(); @@ -1320,16 +1354,6 @@ public class HTMLDocument extends DefaultStyledDocument new char[0], 0, 0); parseBuffer.add(element); } - // If the previous tag is content and the parent is p-implied, then - // we must also close the p-implied. - else if (parseStack.peek() == HTML.Tag.IMPLIED) - { - element = new DefaultStyledDocument.ElementSpec(null, - DefaultStyledDocument.ElementSpec.EndTagType); - parseBuffer.addElement(element); - if (parseStack.size() > 0) - parseStack.pop(); - } element = new DefaultStyledDocument.ElementSpec(null, DefaultStyledDocument.ElementSpec.EndTagType); @@ -1369,27 +1393,6 @@ public class HTMLDocument extends DefaultStyledDocument DefaultStyledDocument.ElementSpec element; AttributeSet attributes = null; - // Content must always be embedded inside a paragraph element, - // so we create this if the previous element is not one of - // <p>, <h1> .. <h6>. - boolean createImpliedParagraph = false; - HTML.Tag parent = (HTML.Tag) parseStack.peek(); - if (parent != HTML.Tag.P && parent != HTML.Tag.H1 - && parent != HTML.Tag.H2 - && parent != HTML.Tag.H3 && parent != HTML.Tag.H4 - && parent != HTML.Tag.H5 && parent != HTML.Tag.H6 - && parent != HTML.Tag.TD) - { - attributes = ctx.getEmptySet(); - attributes = ctx.addAttribute(attributes, - StyleConstants.NameAttribute, - HTML.Tag.IMPLIED); - element = new DefaultStyledDocument.ElementSpec(attributes, - DefaultStyledDocument.ElementSpec.StartTagType); - parseBuffer.add(element); - parseStack.push(HTML.Tag.IMPLIED); - } - // Copy the attribute set, don't use the same object because // it may change if (charAttr != null) @@ -1433,14 +1436,14 @@ public class HTMLDocument extends DefaultStyledDocument // Migrate from the rather htmlAttributeSet to the faster, lighter and // unchangeable alternative implementation. AttributeSet copy = a.copyAttributes(); - - // TODO: Figure out why we must always insert this single character - // (otherwise the element does not appear). Either fix or add explaining - // comment or at least report a normal bug. - DefaultStyledDocument.ElementSpec spec; - spec = new DefaultStyledDocument.ElementSpec(copy, - DefaultStyledDocument.ElementSpec.ContentType, - new char[] {' '}, 0, 1 ); + + // The two spaces are required because some special elements like HR + // must be broken. At least two characters are needed to break into the + // two parts. + DefaultStyledDocument.ElementSpec spec = + new DefaultStyledDocument.ElementSpec(copy, + DefaultStyledDocument.ElementSpec.ContentType, + new char[] {' ', ' '}, 0, 2 ); parseBuffer.add(spec); } @@ -1481,7 +1484,61 @@ public class HTMLDocument extends DefaultStyledDocument HTML.Tag insertTag) { return new HTMLReader(pos, popDepth, pushDepth, insertTag); - } + } + + /** + * Gets the reader for the parser to use when inserting the HTML fragment into + * the document. Checks if the parser is present, sets the parent in the + * element stack and removes any actions for BODY (it can be only one body in + * a HTMLDocument). + * + * @param pos - the starting position + * @param popDepth - the number of EndTagTypes to generate before inserting + * @param pushDepth - the number of StartTagTypes with a direction of + * JoinNextDirection that should be generated before inserting, but + * after the end tags have been generated. + * @param insertTag - the first tag to start inserting into document + * @param parent the element that will be the parent in the document. HTML + * parsing includes checks for the parent, so it must be available. + * @return - the reader + * @throws IllegalStateException if the parsert is not set. + */ + public HTMLEditorKit.ParserCallback getInsertingReader(int pos, int popDepth, + int pushDepth, + HTML.Tag insertTag, + final Element parent) + throws IllegalStateException + { + if (parser == null) + throw new IllegalStateException("Parser has not been set"); + + HTMLReader reader = new HTMLReader(pos, popDepth, pushDepth, insertTag) + { + /** + * Ignore BODY. + */ + public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) + { + if (t != HTML.Tag.BODY) + super.handleStartTag(t, a, pos); + } + + /** + * Ignore BODY. + */ + public void handleEndTag(HTML.Tag t, int pos) + { + if (t != HTML.Tag.BODY) + super.handleEndTag(t, pos); + } + }; + + // Set the parent HTML tag. + reader.parseStack.push(parent.getAttributes().getAttribute( + StyleConstants.NameAttribute)); + + return reader; + } /** * Gets the child element that contains the attribute with the value or null. @@ -1490,8 +1547,8 @@ public class HTMLDocument extends DefaultStyledDocument * @param e - the element to begin search at * @param attribute - the desired attribute * @param value - the desired value - * @return the element found with the attribute and value specified or null - * if it is not found. + * @return the element found with the attribute and value specified or null if + * it is not found. */ public Element getElement(Element e, Object attribute, Object value) { @@ -1516,16 +1573,17 @@ public class HTMLDocument extends DefaultStyledDocument } /** - * Returns the element that has the given id Attribute. If it is not found, - * null is returned. This method works on an Attribute, not a character tag. - * This is not thread-safe. + * Returns the element that has the given id Attribute (for instance, <p id + * ='my paragraph >'). If it is not found, null is returned. The HTML tag, + * having this attribute, is not checked by this method and can be any. The + * method is not thread-safe. * - * @param attrId - the Attribute id to look for + * @param attrId - the value of the attribute id to look for * @return the element that has the given id. */ public Element getElement(String attrId) { - return getElement(getDefaultRootElement(), HTML.getAttributeKey(attrId), + return getElement(getDefaultRootElement(), HTML.Attribute.ID, attrId); } @@ -1542,22 +1600,30 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IllegalStateException - if an HTMLEditorKit.Parser has not been set */ public void setInnerHTML(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException + throws BadLocationException, IOException { if (elem.isLeaf()) throw new IllegalArgumentException("Element is a leaf"); - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("setInnerHTML not implemented"); + + int start = elem.getStartOffset(); + int end = elem.getEndOffset(); + + HTMLEditorKit.ParserCallback reader = getInsertingReader( + end, 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); + + // Remove the previous content + remove(start, end - start); } /** - * Replaces the given element in the parent with the string. When replacing - * a leaf, this will attempt to make sure there is a newline present if one is - * needed. This may result in an additional element being inserted. - * This will be seen as at least two events, n inserts followed by a remove. - * The HTMLEditorKit.Parser must be set. + * Replaces the given element in the parent with the string. When replacing a + * leaf, this will attempt to make sure there is a newline present if one is + * needed. This may result in an additional element being inserted. This will + * be seen as at least two events, n inserts followed by a remove. The + * HTMLEditorKit.Parser must be set. * * @param elem - the branch element whose parent will be replaced * @param htmlText - the string to be parsed and assigned to elem @@ -1565,18 +1631,25 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IOException * @throws IllegalStateException - if parser is not set */ - public void setOuterHTML(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException - { - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("setOuterHTML not implemented"); - } +public void setOuterHTML(Element elem, String htmlText) + throws BadLocationException, IOException + { + // Remove the current element: + int start = elem.getStartOffset(); + int end = elem.getEndOffset(); + + remove(start, end-start); + + HTMLEditorKit.ParserCallback reader = getInsertingReader( + start, 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); + } /** - * Inserts the string before the start of the given element. - * The parser must be set. + * Inserts the string before the start of the given element. The parser must + * be set. * * @param elem - the element to be the root for the new text. * @param htmlText - the string to be parsed and assigned to elem @@ -1585,18 +1658,19 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IllegalStateException - if parser has not been set */ public void insertBeforeStart(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException + throws BadLocationException, IOException { - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("insertBeforeStart not implemented"); + HTMLEditorKit.ParserCallback reader = getInsertingReader( + elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); } /** - * Inserts the string at the end of the element. If elem's children - * are leaves, and the character at elem.getEndOffset() - 1 is a newline, - * then it will be inserted before the newline. The parser must be set. + * Inserts the string at the end of the element. If elem's children are + * leaves, and the character at elem.getEndOffset() - 1 is a newline, then it + * will be inserted before the newline. The parser must be set. * * @param elem - the element to be the root for the new text * @param htmlText - the text to insert @@ -1605,12 +1679,14 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IllegalStateException - if parser is not set */ public void insertBeforeEnd(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException + throws BadLocationException, IOException { - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("insertBeforeEnd not implemented"); + HTMLEditorKit.ParserCallback reader = getInsertingReader( + elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); + } /** @@ -1624,12 +1700,13 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IllegalStateException - if parser is not set */ public void insertAfterEnd(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException + throws BadLocationException, IOException { - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("insertAfterEnd not implemented"); + HTMLEditorKit.ParserCallback reader = getInsertingReader( + elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); } /** @@ -1643,11 +1720,12 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IllegalStateException - if parser is not set */ public void insertAfterStart(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException + throws BadLocationException, IOException { - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("insertAfterStart not implemented"); + HTMLEditorKit.ParserCallback reader = getInsertingReader( + elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); } } diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java index adda4922d57..5d77be8fdd4 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java +++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java @@ -71,6 +71,11 @@ import javax.swing.text.View; import javax.swing.text.ViewFactory; import javax.swing.text.html.parser.ParserDelegator; +/* Move these imports here after javax.swing.text.html to make it compile + with jikes. */ +import gnu.javax.swing.text.html.parser.GnuParserDelegator; +import gnu.javax.swing.text.html.parser.HTML_401Swing; + /** * @author Lillian Angel (langel at redhat dot com) */ @@ -557,19 +562,18 @@ public class HTMLEditorKit else if (tag == HTML.Tag.HEAD) view = new NullView(element); else if (tag.equals(HTML.Tag.TABLE)) - view = new HTMLTableView(element); + view = new javax.swing.text.html.TableView(element); else if (tag.equals(HTML.Tag.TD)) view = new ParagraphView(element); - + else if (tag.equals(HTML.Tag.HR)) + view = new HRuleView(element); + else if (tag.equals(HTML.Tag.BR)) + view = new BRView(element); /* else if (tag.equals(HTML.Tag.MENU) || tag.equals(HTML.Tag.DIR) || tag.equals(HTML.Tag.UL) || tag.equals(HTML.Tag.OL)) view = new ListView(element); - else if (tag.equals(HTML.Tag.HR)) - view = new HRuleView(element); - else if (tag.equals(HTML.Tag.BR)) - view = new BRView(element); else if (tag.equals(HTML.Tag.INPUT) || tag.equals(HTML.Tag.SELECT) || tag.equals(HTML.Tag.TEXTAREA)) view = new FormView(element); @@ -887,7 +891,9 @@ public class HTMLEditorKit protected Parser getParser() { if (parser == null) - parser = new ParserDelegator(); + { + parser = new GnuParserDelegator(HTML_401Swing.getInstance()); + } return parser; } diff --git a/libjava/classpath/javax/swing/text/html/StyleSheet.java b/libjava/classpath/javax/swing/text/html/StyleSheet.java index 2466a2808fe..d92abde7825 100644 --- a/libjava/classpath/javax/swing/text/html/StyleSheet.java +++ b/libjava/classpath/javax/swing/text/html/StyleSheet.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.text.html; +import gnu.javax.swing.text.html.CharacterAttributeTranslator; + import java.awt.Color; import java.awt.Font; import java.awt.Graphics; @@ -585,47 +587,15 @@ public class StyleSheet extends StyleContext } /** - * Converst a color string to a color. If it is not found, null is returned. - * - * @param color - the color string such as "RED" or "#NNNNNN" - * @return the Color, or null if not found. - */ - public Color stringToColor(String color) - { - color = color.toLowerCase(); - if (color.equals("black") || color.equals("#000000")) - return Color.BLACK; - else if (color.equals("aqua") || color.equals("#00FFFF")) - return new Color(127, 255, 212); - else if (color.equals("gray") || color.equals("#808080")) - return Color.GRAY; - else if (color.equals("navy") || color.equals("#000080")) - return new Color(0, 0, 128); - else if (color.equals("silver") || color.equals("#C0C0C0")) - return Color.LIGHT_GRAY; - else if (color.equals("green") || color.equals("#008000")) - return Color.GREEN; - else if (color.equals("olive") || color.equals("#808000")) - return new Color(128, 128, 0); - else if (color.equals("teal") || color.equals("#008080")) - return new Color(0, 128, 128); - else if (color.equals("blue") || color.equals("#0000FF")) - return Color.BLUE; - else if (color.equals("lime") || color.equals("#00FF00")) - return new Color(0, 255, 0); - else if (color.equals("purple") || color.equals("#800080")) - return new Color(128, 0, 128); - else if (color.equals("white") || color.equals("#FFFFFF")) - return Color.WHITE; - else if (color.equals("fuchsia") || color.equals("#FF00FF")) - return Color.MAGENTA; - else if (color.equals("maroon") || color.equals("#800000")) - return new Color(128, 0, 0); - else if (color.equals("Red") || color.equals("#FF0000")) - return Color.RED; - else if (color.equals("Yellow") || color.equals("#FFFF00")) - return Color.YELLOW; - return null; + * Convert the color string represenation into java.awt.Color. The valid + * values are like "aqua" , "#00FFFF" or "rgb(1,6,44)". + * + * @param colorName the color to convert. + * @return the matching java.awt.color + */ + public Color stringToColor(String colorName) + { + return CharacterAttributeTranslator.getColor(colorName); } /** diff --git a/libjava/classpath/javax/swing/text/html/TableView.java b/libjava/classpath/javax/swing/text/html/TableView.java new file mode 100644 index 00000000000..c2edc8cdd64 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/TableView.java @@ -0,0 +1,137 @@ +/* TableView.java -- A table view for HTML tables + 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 javax.swing.text.html; + +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; + +/** + * A conrete implementation of TableView that renders HTML tables. + * + * @author Roman Kennke (kennke@aicas.com) + */ +class TableView + extends javax.swing.text.TableView +{ + /** + * Represents a single table row. + */ + public class RowView extends TableRow + { + /** + * Creates a new instance of the <code>RowView</code>. + * + * @param el the element for which to create a row view + */ + public RowView(Element el) + { + super(el); + } + + /** + * Get the associated style sheet from the document. + * + * @return the associated style sheet. + */ + protected StyleSheet getStyleSheet() + { + Document d = getElement().getDocument(); + if (d instanceof HTMLDocument) + return ((HTMLDocument) d).getStyleSheet(); + else + return null; + } + } + + /** + * Creates a new HTML table view for the specified element. + * + * @param el the element for the table view + */ + public TableView(Element el) + { + super(el); + } + + /** + * Get the associated style sheet from the document. + * + * @return the associated style sheet. + */ + protected StyleSheet getStyleSheet() + { + Document d = getElement().getDocument(); + if (d instanceof HTMLDocument) + return ((HTMLDocument) d).getStyleSheet(); + else + return null; + } + + /** + * Creates a view for a table row. + * + * @param el the element that represents the table row + * @return a view for rendering the table row + * (and instance of {@link RowView}). + */ + protected TableRow createTableRow(Element el) + { + return new RowView(el); + } + + /** + * Loads the children of the Table. This completely bypasses the ViewFactory + * and creates instances of TableRow instead. + * + * @param vf ignored + */ + protected void loadChildren(ViewFactory vf) + { + Element el = getElement(); + int numChildren = el.getElementCount(); + View[] rows = new View[numChildren]; + for (int i = 0; i < numChildren; ++i) + { + rows[i] = createTableRow(el.getElement(i)); + } + replace(0, getViewCount(), rows); + } +} diff --git a/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java b/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java index e5d2db4df7c..70636d92923 100644 --- a/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java +++ b/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java @@ -52,9 +52,6 @@ import javax.swing.text.html.HTMLEditorKit.ParserCallback; * This class instantiates and starts the working instance of * html parser, being responsible for providing the default DTD. * - * TODO Later this class must be derived from the totally abstract class - * HTMLEditorKit.Parser. HTMLEditorKit that does not yet exist. - * * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) */ public class ParserDelegator diff --git a/libjava/classpath/javax/swing/text/rtf/RTFParser.java b/libjava/classpath/javax/swing/text/rtf/RTFParser.java index 4f0f967c117..de1b1c6ff15 100644 --- a/libjava/classpath/javax/swing/text/rtf/RTFParser.java +++ b/libjava/classpath/javax/swing/text/rtf/RTFParser.java @@ -140,9 +140,17 @@ class RTFParser parseHeader(); parseDocument(); - Token t2 = scanner.readToken(); - if (t2.type != Token.RCURLY) - throw new RTFParseException("expected right curly braces"); + Token t2 = scanner.peekToken(); + if (t2.type == Token.RCURLY) + { + // Eat the token. + scanner.readToken(); + } + else + { + // Ignore this for maximum robustness when file is broken. + System.err.println("RTF warning: expected right curly braces"); + } } diff --git a/libjava/classpath/javax/swing/text/rtf/RTFScanner.java b/libjava/classpath/javax/swing/text/rtf/RTFScanner.java index 3cdd6e8e0b9..060e087eb67 100644 --- a/libjava/classpath/javax/swing/text/rtf/RTFScanner.java +++ b/libjava/classpath/javax/swing/text/rtf/RTFScanner.java @@ -71,6 +71,11 @@ class RTFScanner private StringBuffer buffer; /** + * Lookahead token. + */ + private Token lastToken; + + /** * Constructs a new RTFScanner without initializing the {@link Reader}. */ private RTFScanner() @@ -120,7 +125,7 @@ class RTFScanner * * @throws IOException if the underlying stream has problems */ - public Token readToken() + private Token readTokenImpl() throws IOException { Token token = null; @@ -156,6 +161,27 @@ class RTFScanner return token; } + Token peekToken() + throws IOException + { + lastToken = readTokenImpl(); + return lastToken; + } + + Token readToken() + throws IOException + { + Token token; + if (lastToken != null) + { + token = lastToken; + lastToken = null; + } + else + token = readTokenImpl(); + return token; + } + /** * Reads in a control word and optional parameter. * diff --git a/libjava/classpath/javax/swing/tree/TreePath.java b/libjava/classpath/javax/swing/tree/TreePath.java index 93b59b07edf..1c4d78787f4 100644 --- a/libjava/classpath/javax/swing/tree/TreePath.java +++ b/libjava/classpath/javax/swing/tree/TreePath.java @@ -174,7 +174,7 @@ public class TreePath implements Serializable return false; for (index = 0; index < path.length; index++) { - if (!treepath[index].equals(path[index])) + if (!path[index].equals(treepath[index])) return false; } |