This commit is contained in:
J. Duke 2017-07-05 16:59:02 +02:00
commit 94b9558f88
291 changed files with 30694 additions and 1970 deletions

View File

@ -43,3 +43,4 @@ e01380cd1de4ce048b87d059d238e5ab5e341947 jdk7-b65
6bad5e3fe50337d95b1416d744780d65bc570da6 jdk7-b66
c4523c6f82048f420bf0d57c4cd47976753b7d2c jdk7-b67
e1b972ff53cd58f825791f8ed9b2deffd16e768c jdk7-b68
82e6c820c51ac27882b77755d42efefdbf1dcda0 jdk7-b69

View File

@ -32,7 +32,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
--- end of LICENSE file ---
%% This notice is provided with respect to ASM, which may be included with this software:
Copyright (c) 2000-2005 INRIA, France Telecom
Copyright (c) 2000-2007 INRIA, France Telecom
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -43,3 +43,4 @@ d20e45cd539f20405ff843652069cfd7550c5ab3 jdk7-b63
a821e059a961bcb02830280d51f6dd030425c066 jdk7-b66
a12ea7c7b497b4ba7830550095ef633bd6f43971 jdk7-b67
5182bcc9c60cac429d1f7988676cec7320752be3 jdk7-b68
8120d308ec4e805c5588b8d9372844d781c4112d jdk7-b69

View File

@ -32,7 +32,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
--- end of LICENSE file ---
%% This notice is provided with respect to ASM, which may be included with this software:
Copyright (c) 2000-2005 INRIA, France Telecom
Copyright (c) 2000-2007 INRIA, France Telecom
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -43,3 +43,4 @@ ba313800759b678979434d6da8ed3bf49eb8bea4 jdk7-b65
57c71ad0341b8b64ed20f81151eb7f06324f8894 jdk7-b66
18f526145aea355a9320b724373386fc2170f183 jdk7-b67
d07e68298d4e17ebf93d8299e43fcc3ded26472a jdk7-b68
54fd4d9232969ea6cd3d236e5ad276183bb0d423 jdk7-b69

View File

@ -32,7 +32,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
--- end of LICENSE file ---
%% This notice is provided with respect to ASM, which may be included with this software:
Copyright (c) 2000-2005 INRIA, France Telecom
Copyright (c) 2000-2007 INRIA, France Telecom
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -43,3 +43,4 @@ a50217eb3ee10b9f9547e0708e5c9625405083ef jdk7-b64
bd31b30a5b21f20e42965b1633f18a5c7946d398 jdk7-b66
a952aafd5181af953b0ef3010dbd2fcc28460e8a jdk7-b67
b23d905cb5d3b382295240d28ab0bfb266b4503c jdk7-b68
226b20019b1f020c09ea97d137d98e011ce65d76 jdk7-b69

View File

@ -32,7 +32,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
--- end of LICENSE file ---
%% This notice is provided with respect to ASM, which may be included with this software:
Copyright (c) 2000-2005 INRIA, France Telecom
Copyright (c) 2000-2007 INRIA, France Telecom
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -77,6 +77,7 @@ JAVA_JAVA_java = \
java/lang/Compiler.java \
java/lang/Throwable.java \
java/lang/Exception.java \
java/lang/ReflectiveOperationException.java \
java/lang/IllegalAccessException.java \
java/lang/InstantiationException.java \
java/lang/ClassNotFoundException.java \
@ -250,6 +251,8 @@ JAVA_JAVA_java = \
java/util/IdentityHashMap.java \
java/util/EnumMap.java \
java/util/Arrays.java \
java/util/TimSort.java \
java/util/ComparableTimSort.java \
java/util/ConcurrentModificationException.java \
java/util/ServiceLoader.java \
java/util/ServiceConfigurationError.java \

View File

@ -41,6 +41,7 @@ FILES_java = \
sun/net/NetProperties.java \
sun/net/NetHooks.java \
sun/net/util/IPAddressUtil.java \
sun/net/util/URLUtil.java \
sun/net/dns/ResolverConfiguration.java \
sun/net/dns/ResolverConfigurationImpl.java \
sun/net/ftp/FtpClient.java \

View File

@ -1,5 +1,5 @@
#
# Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
# Copyright 1996-2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -60,7 +60,7 @@ ifeq ($(PLATFORM), windows)
endif
endif
SUBDIRS = other action util tools jgss krb5 smartcardio $(PKCS11) \
SUBDIRS = ec other action util tools jgss krb5 smartcardio $(PKCS11) \
$(JGSS_WRAPPER) $(MSCAPI)
all build clean clobber::

View File

@ -0,0 +1,54 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
FILES_c = \
ec.c \
ec2_163.c \
ec2_193.c \
ec2_233.c \
ec2_aff.c \
ec2_mont.c \
ecdecode.c \
ecl.c \
ecl_curve.c \
ecl_gf.c \
ecl_mult.c \
ec_naf.c \
ecp_192.c \
ecp_224.c \
ecp_256.c \
ecp_384.c \
ecp_521.c \
ecp_aff.c \
ecp_jac.c \
ecp_jm.c \
ecp_mont.c \
mp_gf2m.c \
mpi.c \
mplogic.c \
mpmontg.c \
oid.c \
secitem.c

View File

@ -0,0 +1,319 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
#
# Makefile for building sunec.jar and sunecc native library.
#
# This file was derived from make/com/sun/crypto/provider/Makefile.
#
#
# (The terms "OpenJDK" and "JDK" below refer to OpenJDK and Sun JDK builds
# respectively.)
#
# JCE builds are very different between OpenJDK and JDK. The OpenJDK JCE
# jar files do not require signing, but those for JDK do. If an unsigned
# jar file is installed into JDK, things will break when the crypto
# routines are called.
#
# This Makefile does the "real" build of the JCE files. For OpenJDK,
# the jar files built here are installed directly into the OpenJDK.
#
# For JDK, the binaries use pre-built/pre-signed binary files stored in
# the closed workspace that are not shipped in the OpenJDK workspaces.
# We still build the JDK files here to verify the files compile, and in
# preparation for possible signing. Developers working on JCE in JDK
# must sign the JCE files before testing. The JCE signing key is kept
# separate from the JDK workspace to prevent its disclosure.
#
# SPECIAL NOTE TO JCE/JDK developers: The source files must eventually
# be built, signed, and then the resulting jar files MUST BE CHECKED
# INTO THE CLOSED PART OF THE WORKSPACE*. This separate step *MUST NOT
# BE FORGOTTEN*, otherwise a bug fixed in the source code will not be
# reflected in the shipped binaries. The "release" target should be
# used to generate the required files.
#
# There are a number of targets to help both JDK/OpenJDK developers.
#
# Main Targets (JDK/OPENJDK):
#
# all/clobber/clean The usual, plus the native libraries.
# If OpenJDK, installs sunec.jar.
# If JDK, installs prebuilt
# sunec.jar.
#
# jar Builds/installs sunec.jar
# If OpenJDK, does not sign.
# If JDK, tries to sign.
#
# Other lesser-used Targets (JDK/OPENJDK):
#
# build-jar Builds sunec.jar
# (does not sign/install)
#
# install-jar Alias for "jar" above.
#
# Other targets (JDK only):
#
# sign Alias for sign-jar
# sign-jar Builds/signs sunec.jar (no install)
#
# release Builds all targets in preparation
# for workspace integration.
#
# install-prebuilt Installs the pre-built jar files
#
# This makefile was written to support parallel target execution.
#
BUILDDIR = ../../..
PACKAGE = sun.security.ec
PRODUCT = sun
#
# The following is for when we need to do postprocessing
# (signing) against a read-only build. If the OUTPUTDIR
# isn't writable, the build currently crashes out.
#
ifndef OPENJDK
ifdef ALT_JCE_BUILD_DIR
# =====================================================
# Where to place the output, in case we're building from a read-only
# build area. (e.g. a release engineering build.)
JCE_BUILD_DIR=${ALT_JCE_BUILD_DIR}
IGNORE_WRITABLE_OUTPUTDIR_TEST=true
else
JCE_BUILD_DIR=${TEMPDIR}
endif
endif
include $(BUILDDIR)/common/Defs.gmk
#
# Location for the newly built classfiles.
#
CLASSDESTDIR = $(TEMPDIR)/classes
#
# Java files
#
AUTO_FILES_JAVA_DIRS = $(PKGDIR)
include $(BUILDDIR)/common/Classes.gmk
#
# Some licensees do not get the native ECC sources, but we still need to
# be able to build "all" for them. Check here to see if the sources are
# available. If not, then skip them.
#
NATIVE_ECC_AVAILABLE := $(shell \
if [ -d $(SHARE_SRC)/native/$(PKGDIR) ] ; then \
$(ECHO) true; \
else \
$(ECHO) false; \
fi)
ifeq ($(NATIVE_ECC_AVAILABLE), true)
LIBRARY = sunecc
#
# Java files that define native methods
#
FILES_export = \
$(PKGDIR)/ECDHKeyAgreement.java \
$(PKGDIR)/ECDSASignature.java \
$(PKGDIR)/ECKeyPairGenerator.java
JAVAHFLAGS += -classpath $(CLASSDESTDIR)
#
# C and C++ files
#
include FILES_c.gmk
FILES_cpp = ECC_JNI.cpp
CPLUSPLUSLIBRARY=true
FILES_m = mapfile-vers
#
# Find native code
#
vpath %.cpp $(SHARE_SRC)/native/$(PKGDIR)
vpath %.c $(SHARE_SRC)/native/$(PKGDIR)
#
# Find include files
#
OTHER_INCLUDES += -I$(SHARE_SRC)/native/$(PKGDIR)
#
# Compiler flags
#
OTHER_CFLAGS += -DMP_API_COMPATIBLE -DNSS_ECC_MORE_THAN_SUITE_B
#
# Libraries to link
#
ifeq ($(PLATFORM), windows)
OTHER_LDLIBS += $(JVMLIB)
else
OTHER_LDLIBS = -ldl $(JVMLIB) $(LIBCXX)
endif
include $(BUILDDIR)/common/Mapfile-vers.gmk
include $(BUILDDIR)/common/Library.gmk
endif # NATIVE_ECC_AVAILABLE
#
# We use a variety of subdirectories in the $(TEMPDIR) depending on what
# part of the build we're doing. Both OPENJDK/JDK builds are initially
# done in the unsigned area. When files are signed in JDK,
# they will be placed in the appropriate area.
#
UNSIGNED_DIR = $(TEMPDIR)/unsigned
include $(BUILDDIR)/javax/crypto/Defs-jce.gmk
#
# Rules
#
ifdef OPENJDK
all: build-jar install-jar
else
all: build-jar install-prebuilt
$(build-warning)
endif
# =====================================================
# Build the unsigned sunec.jar file.
#
JAR_DESTFILE = $(EXTDIR)/sunec.jar
#
# Since the -C option to jar is used below, each directory entry must be
# preceded with the appropriate directory to "cd" into.
#
JAR_DIRS = $(patsubst %, -C $(CLASSDESTDIR) %, $(AUTO_FILES_JAVA_DIRS))
build-jar: $(UNSIGNED_DIR)/sunec.jar
#
# Build sunec.jar.
#
$(UNSIGNED_DIR)/sunec.jar: build
$(prep-target)
$(BOOT_JAR_CMD) cf $@ $(JAR_DIRS) \
$(BOOT_JAR_JFLAGS)
@$(java-vm-cleanup)
ifndef OPENJDK
# =====================================================
# Sign the provider jar file. Not needed for OpenJDK.
#
SIGNED_DIR = $(JCE_BUILD_DIR)/signed
sign: sign-jar
sign-jar: $(SIGNED_DIR)/sunec.jar
ifndef ALT_JCE_BUILD_DIR
$(SIGNED_DIR)/sunec.jar: $(UNSIGNED_DIR)/sunec.jar
else
#
# We have to remove the build dependency, otherwise, we'll try to rebuild it
# which we can't do on a read-only filesystem.
#
$(SIGNED_DIR)/sunec.jar:
@if [ ! -r $(UNSIGNED_DIR)/sunec.jar ] ; then \
$(ECHO) "Couldn't find $(UNSIGNED_DIR)/sunec.jar"; \
exit 1; \
fi
endif
$(call sign-file, $(UNSIGNED_DIR)/sunec.jar)
# =====================================================
# Create the Release Engineering files. Signed builds, etc.
#
release: $(SIGNED_DIR)/sunec.jar
$(RM) $(JCE_BUILD_DIR)/release/sunec.jar
$(MKDIR) -p $(JCE_BUILD_DIR)/release
$(CP) $(SIGNED_DIR)/sunec.jar $(JCE_BUILD_DIR)/release
$(release-warning)
endif # OPENJDK
# =====================================================
# Install routines.
#
#
# Install sunec.jar, depending on which type is requested.
#
install-jar jar: $(JAR_DESTFILE)
ifndef OPENJDK
$(release-warning)
endif
ifdef OPENJDK
$(JAR_DESTFILE): $(UNSIGNED_DIR)/sunec.jar
else
$(JAR_DESTFILE): $(SIGNED_DIR)/sunec.jar
endif
$(install-file)
ifndef OPENJDK
install-prebuilt:
@$(ECHO) "\n>>>Installing prebuilt SunEC provider..."
$(RM) $(JAR_DESTFILE)
$(CP) $(PREBUILT_DIR)/ec/sunec.jar $(JAR_DESTFILE)
endif
# =====================================================
# Support routines.
#
clobber clean::
$(RM) -r $(JAR_DESTFILE) $(TEMPDIR) $(JCE_BUILD_DIR)
.PHONY: build-jar jar install-jar
ifndef OPENJDK
.PHONY: sign sign-jar release install-prebuilt
endif

View File

@ -0,0 +1,37 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
# Define public interface.
SUNWprivate_1.1 {
global:
Java_sun_security_ec_ECKeyPairGenerator_generateECKeyPair;
Java_sun_security_ec_ECKeyPairGenerator_getEncodedBytes;
Java_sun_security_ec_ECDSASignature_signDigest;
Java_sun_security_ec_ECDSASignature_verifySignedDigest;
Java_sun_security_ec_ECDHKeyAgreement_deriveKey;
local:
*;
};

View File

@ -1,5 +1,5 @@
#
# Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
# Copyright 1996-2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -33,7 +33,6 @@ include $(BUILDDIR)/common/Defs.gmk
#
AUTO_FILES_JAVA_DIRS = \
sun/security/acl \
sun/security/ec \
sun/security/jca \
sun/security/pkcs \
sun/security/pkcs12 \

View File

@ -1 +1,187 @@
0x15 U+0085
#
# The diff of 01A434B0.TXMAP110 and 34B001A4.RXMAP110
#
# Added: 0x15 U+0085
#
0x15 U+0085
0x42 U+FE7C
0x46 U+FE80
0x47 U+FE81
0x49 U+FE83
0x4B U+066C
0x4B U+FF0E
0x4C U+FF1C
0x4D U+FF08
0x4E U+FF0B
0x4F U+FF5C
0x50 U+FF06
0x52 U+FE85
0x52 U+FE86
0x55 U+FE89
0x55 U+FE8A
0x55 U+FE8B
0x55 U+FE8C
0x56 U+0625
0x56 U+FE87
0x56 U+FE8D
0x57 U+FE88
0x58 U+FE8F
0x58 U+FE90
0x59 U+FE92
0x5A U+FF01
0x5B U+FF04
0x5C U+066D
0x5C U+FF0A
0x5D U+FF09
0x5E U+FF1B
0x60 U+FF0D
0x61 U+FF0F
0x62 U+FE93
0x62 U+FE94
0x63 U+FE95
0x63 U+FE96
0x64 U+FE98
0x65 U+FE99
0x65 U+FE9A
0x66 U+FE9C
0x67 U+FE9D
0x67 U+FE9E
0x68 U+FEA0
0x69 U+FEA1
0x69 U+FEA2
0x6B U+066B
0x6B U+FF0C
0x6C U+066A
0x6C U+FF05
0x6D U+FF3F
0x6E U+FF1E
0x6F U+FF1F
0x70 U+FEA4
0x71 U+FEA5
0x71 U+FEA6
0x72 U+FEA8
0x73 U+FEA9
0x73 U+FEAA
0x74 U+FEAB
0x74 U+FEAC
0x75 U+FEAD
0x75 U+FEAE
0x76 U+FEAF
0x76 U+FEB0
0x77 U+FEB1
0x77 U+FEB2
0x78 U+FEB4
0x7A U+FF1A
0x7B U+FF03
0x7C U+FF20
0x7D U+FF07
0x7E U+FF1D
0x7F U+FF02
0x80 U+FEB5
0x80 U+FEB6
0x81 U+FF41
0x82 U+FF42
0x83 U+FF43
0x84 U+FF44
0x85 U+FF45
0x86 U+FF46
0x87 U+FF47
0x88 U+FF48
0x89 U+FF49
0x8A U+FEB8
0x8B U+FEB9
0x8B U+FEBA
0x8C U+FEBC
0x8D U+FEBD
0x8D U+FEBE
0x8E U+FEC0
0x8F U+FEC1
0x8F U+FEC2
0x8F U+FEC3
0x8F U+FEC4
0x90 U+FEC5
0x90 U+FEC6
0x90 U+FEC7
0x90 U+FEC8
0x91 U+FF4A
0x92 U+FF4B
0x93 U+FF4C
0x94 U+FF4D
0x95 U+FF4E
0x96 U+FF4F
0x97 U+FF50
0x98 U+FF51
0x99 U+FF52
0x9A U+FEC9
0x9E U+FECD
0xA2 U+FF53
0xA3 U+FF54
0xA4 U+FF55
0xA5 U+FF56
0xA6 U+FF57
0xA7 U+FF58
0xA8 U+FF59
0xA9 U+FF5A
0xAB U+FED1
0xAB U+FED2
0xAC U+FED4
0xAD U+FED5
0xAD U+FED6
0xAE U+FED8
0xAF U+FED9
0xAF U+FEDA
0xB0 U+FEDC
0xB1 U+FEDD
0xB1 U+FEDE
0xB8 U+FEF9
0xB9 U+FEFA
0xBA U+FEE0
0xBB U+FEE1
0xBB U+FEE2
0xBC U+FEE4
0xBD U+FEE5
0xBD U+FEE6
0xBE U+FEE8
0xBF U+FEE9
0xBF U+FEEA
0xC1 U+FF21
0xC2 U+FF22
0xC3 U+FF23
0xC4 U+FF24
0xC5 U+FF25
0xC6 U+FF26
0xC7 U+FF27
0xC8 U+FF28
0xC9 U+FF29
0xCF U+FEED
0xCF U+FEEE
0xD1 U+FF2A
0xD2 U+FF2B
0xD3 U+FF2C
0xD4 U+FF2D
0xD5 U+FF2E
0xD6 U+FF2F
0xD7 U+FF30
0xD8 U+FF31
0xD9 U+FF32
0xDA U+FEEF
0xDC U+FEF1
0xDE U+FEF4
0xE2 U+FF33
0xE3 U+FF34
0xE4 U+FF35
0xE5 U+FF36
0xE6 U+FF37
0xE7 U+FF38
0xE8 U+FF39
0xE9 U+FF3A
0xF0 U+FF10
0xF1 U+FF11
0xF2 U+FF12
0xF3 U+FF13
0xF4 U+FF14
0xF5 U+FF15
0xF6 U+FF16
0xF7 U+FF17
0xF8 U+FF18
0xF9 U+FF19

View File

@ -1,257 +1,253 @@
#Generated from IBM420.java
0x00 U+0000
0x01 U+0001
0x02 U+0002
0x03 U+0003
0x04 U+009c
0x05 U+0009
0x06 U+0086
0x07 U+007f
0x08 U+0097
0x09 U+008d
0x0a U+008e
0x0b U+000b
0x0c U+000c
0x0d U+000d
0x0e U+000e
0x0f U+000f
0x10 U+0010
0x11 U+0011
0x12 U+0012
0x13 U+0013
0x14 U+009d
0x15 U+000a
0x16 U+0008
0x17 U+0087
0x18 U+0018
0x19 U+0019
0x1a U+0092
0x1b U+008f
0x1c U+001c
0x1d U+001d
0x1e U+001e
0x1f U+001f
0x20 U+0080
0x21 U+0081
0x22 U+0082
0x23 U+0083
0x24 U+0084
0x25 U+000a
0x26 U+0017
0x27 U+001b
0x28 U+0088
0x29 U+0089
0x2a U+008a
0x2b U+008b
0x2c U+008c
0x2d U+0005
0x2e U+0006
0x2f U+0007
0x30 U+0090
0x31 U+0091
0x32 U+0016
0x33 U+0093
0x34 U+0094
0x35 U+0095
0x36 U+0096
0x37 U+0004
0x38 U+0098
0x39 U+0099
0x3a U+009a
0x3b U+009b
0x3c U+0014
0x3d U+0015
0x3e U+009e
0x3f U+001a
0x40 U+0020
0x41 U+00a0
0x42 U+fe7c
0x43 U+fe7d
0x44 U+0640
0x45 U+f8fc
0x46 U+fe80
0x47 U+fe81
0x48 U+fe82
0x49 U+fe83
0x4a U+00a2
0x4b U+002e
0x4c U+003c
0x4d U+0028
0x4e U+002b
0x4f U+007c
0x50 U+0026
0x51 U+fe84
0x52 U+fe85
0x53 U+fffd
0x54 U+fffd
0x55 U+fe8b
0x56 U+fe8d
0x57 U+fe8e
0x58 U+fe8f
0x59 U+fe91
0x5a U+0021
0x5b U+0024
0x5c U+002a
0x5d U+0029
0x5e U+003b
0x5f U+00ac
0x60 U+002d
0x61 U+002f
0x62 U+fe93
0x63 U+fe95
0x64 U+fe97
0x65 U+fe99
0x66 U+fe9b
0x67 U+fe9d
0x68 U+fe9f
0x69 U+fea1
0x6a U+00a6
0x6b U+002c
0x6c U+0025
0x6d U+005f
0x6e U+003e
0x6f U+003f
0x70 U+fea3
0x71 U+fea5
0x72 U+fea7
0x73 U+fea9
0x74 U+feab
0x75 U+fead
0x76 U+feaf
0x77 U+f8f6
0x78 U+feb3
0x79 U+060c
0x7a U+003a
0x7b U+0023
0x7c U+0040
0x7d U+0027
0x7e U+003d
0x7f U+0022
0x80 U+f8f5
0x81 U+0061
0x82 U+0062
0x83 U+0063
0x84 U+0064
0x85 U+0065
0x86 U+0066
0x87 U+0067
0x88 U+0068
0x89 U+0069
0x8a U+feb7
0x8b U+f8f4
0x8c U+febb
0x8d U+f8f7
0x8e U+febf
0x8f U+fec3
0x90 U+fec7
0x91 U+006a
0x92 U+006b
0x93 U+006c
0x94 U+006d
0x95 U+006e
0x96 U+006f
0x97 U+0070
0x98 U+0071
0x99 U+0072
0x9a U+fec9
0x9b U+feca
0x9c U+fecb
0x9d U+fecc
0x9e U+fecd
0x9f U+fece
0xa0 U+fecf
0xa1 U+00f7
0xa2 U+0073
0xa3 U+0074
0xa4 U+0075
0xa5 U+0076
0xa6 U+0077
0xa7 U+0078
0xa8 U+0079
0xa9 U+007a
0xaa U+fed0
0xab U+fed1
0xac U+fed3
0xad U+fed5
0xae U+fed7
0xaf U+fed9
0xb0 U+fedb
0xb1 U+fedd
0xb2 U+fef5
0xb3 U+fef6
0xb4 U+fef7
0xb5 U+fef8
0xb6 U+fffd
0xb7 U+fffd
0xb8 U+fefb
0xb9 U+fefc
0xba U+fedf
0xbb U+fee1
0xbc U+fee3
0xbd U+fee5
0xbe U+fee7
0xbf U+fee9
0xc0 U+061b
0xc1 U+0041
0xc2 U+0042
0xc3 U+0043
0xc4 U+0044
0xc5 U+0045
0xc6 U+0046
0xc7 U+0047
0xc8 U+0048
0xc9 U+0049
0xca U+00ad
0xcb U+feeb
0xcc U+fffd
0xcd U+feec
0xce U+fffd
0xcf U+feed
0xd0 U+061f
0xd1 U+004a
0xd2 U+004b
0xd3 U+004c
0xd4 U+004d
0xd5 U+004e
0xd6 U+004f
0xd7 U+0050
0xd8 U+0051
0xd9 U+0052
0xda U+feef
0xdb U+fef0
0xdc U+fef1
0xdd U+fef2
0xde U+fef3
0xdf U+0660
0xe0 U+00d7
0xe1 U+2007
0xe2 U+0053
0xe3 U+0054
0xe4 U+0055
0xe5 U+0056
0xe6 U+0057
0xe7 U+0058
0xe8 U+0059
0xe9 U+005a
0xea U+0661
0xeb U+0662
0xec U+fffd
0xed U+0663
0xee U+0664
0xef U+0665
0xf0 U+0030
0xf1 U+0031
0xf2 U+0032
0xf3 U+0033
0xf4 U+0034
0xf5 U+0035
0xf6 U+0036
0xf7 U+0037
0xf8 U+0038
0xf9 U+0039
0xfa U+fffd
0xfb U+0666
0xfc U+0667
0xfd U+0668
0xfe U+0669
0xff U+009f
#
# Frm IBMCDC datatable 01A434B0.TXMAP110
#
# Changed
# 0x15 U+0085 -> 0x15 U+000a
#
0x00 U+0000
0x01 U+0001
0x02 U+0002
0x03 U+0003
0x04 U+009C
0x05 U+0009
0x06 U+0086
0x07 U+007F
0x08 U+0097
0x09 U+008D
0x0A U+008E
0x0B U+000B
0x0C U+000C
0x0D U+000D
0x0E U+000E
0x0F U+000F
0x10 U+0010
0x11 U+0011
0x12 U+0012
0x13 U+0013
0x14 U+009D
0x15 U+000A
0x16 U+0008
0x17 U+0087
0x18 U+0018
0x19 U+0019
0x1A U+0092
0x1B U+008F
0x1C U+001C
0x1D U+001D
0x1E U+001E
0x1F U+001F
0x20 U+0080
0x21 U+0081
0x22 U+0082
0x23 U+0083
0x24 U+0084
0x25 U+000A
0x26 U+0017
0x27 U+001B
0x28 U+0088
0x29 U+0089
0x2A U+008A
0x2B U+008B
0x2C U+008C
0x2D U+0005
0x2E U+0006
0x2F U+0007
0x30 U+0090
0x31 U+0091
0x32 U+0016
0x33 U+0093
0x34 U+0094
0x35 U+0095
0x36 U+0096
0x37 U+0004
0x38 U+0098
0x39 U+0099
0x3A U+009A
0x3B U+009B
0x3C U+0014
0x3D U+0015
0x3E U+009E
0x3F U+001A
0x40 U+0020
0x41 U+00A0
0x42 U+0651
0x43 U+FE7D
0x44 U+0640
0x45 U+200B
0x46 U+0621
0x47 U+0622
0x48 U+FE82
0x49 U+0623
0x4A U+00A2
0x4B U+002E
0x4C U+003C
0x4D U+0028
0x4E U+002B
0x4F U+007C
0x50 U+0026
0x51 U+FE84
0x52 U+0624
0x55 U+0626
0x56 U+0627
0x57 U+FE8E
0x58 U+0628
0x59 U+FE91
0x5A U+0021
0x5B U+0024
0x5C U+002A
0x5D U+0029
0x5E U+003B
0x5F U+00AC
0x60 U+002D
0x61 U+002F
0x62 U+0629
0x63 U+062A
0x64 U+FE97
0x65 U+062B
0x66 U+FE9B
0x67 U+062C
0x68 U+FE9F
0x69 U+062D
0x6A U+00A6
0x6B U+002C
0x6C U+0025
0x6D U+005F
0x6E U+003E
0x6F U+003F
0x70 U+FEA3
0x71 U+062E
0x72 U+FEA7
0x73 U+062F
0x74 U+0630
0x75 U+0631
0x76 U+0632
0x77 U+0633
0x78 U+FEB3
0x79 U+060C
0x7A U+003A
0x7B U+0023
0x7C U+0040
0x7D U+0027
0x7E U+003D
0x7F U+0022
0x80 U+0634
0x81 U+0061
0x82 U+0062
0x83 U+0063
0x84 U+0064
0x85 U+0065
0x86 U+0066
0x87 U+0067
0x88 U+0068
0x89 U+0069
0x8A U+FEB7
0x8B U+0635
0x8C U+FEBB
0x8D U+0636
0x8E U+FEBF
0x8F U+0637
0x90 U+0638
0x91 U+006A
0x92 U+006B
0x93 U+006C
0x94 U+006D
0x95 U+006E
0x96 U+006F
0x97 U+0070
0x98 U+0071
0x99 U+0072
0x9A U+0639
0x9B U+FECA
0x9C U+FECB
0x9D U+FECC
0x9E U+063A
0x9F U+FECE
0xA0 U+FECF
0xA1 U+00F7
0xA2 U+0073
0xA3 U+0074
0xA4 U+0075
0xA5 U+0076
0xA6 U+0077
0xA7 U+0078
0xA8 U+0079
0xA9 U+007A
0xAA U+FED0
0xAB U+0641
0xAC U+FED3
0xAD U+0642
0xAE U+FED7
0xAF U+0643
0xB0 U+FEDB
0xB1 U+0644
0xB2 U+FEF5
0xB3 U+FEF6
0xB4 U+FEF7
0xB5 U+FEF8
0xB8 U+FEFB
0xB9 U+FEFC
0xBA U+FEDF
0xBB U+0645
0xBC U+FEE3
0xBD U+0646
0xBE U+FEE7
0xBF U+0647
0xC0 U+061B
0xC1 U+0041
0xC2 U+0042
0xC3 U+0043
0xC4 U+0044
0xC5 U+0045
0xC6 U+0046
0xC7 U+0047
0xC8 U+0048
0xC9 U+0049
0xCA U+00AD
0xCB U+FEEB
0xCD U+FEEC
0xCF U+0648
0xD0 U+061F
0xD1 U+004A
0xD2 U+004B
0xD3 U+004C
0xD4 U+004D
0xD5 U+004E
0xD6 U+004F
0xD7 U+0050
0xD8 U+0051
0xD9 U+0052
0xDA U+0649
0xDB U+FEF0
0xDC U+064A
0xDD U+FEF2
0xDE U+FEF3
0xDF U+0660
0xE0 U+00D7
0xE2 U+0053
0xE3 U+0054
0xE4 U+0055
0xE5 U+0056
0xE6 U+0057
0xE7 U+0058
0xE8 U+0059
0xE9 U+005A
0xEA U+0661
0xEB U+0662
0xED U+0663
0xEE U+0664
0xEF U+0665
0xF0 U+0030
0xF1 U+0031
0xF2 U+0032
0xF3 U+0033
0xF4 U+0034
0xF5 U+0035
0xF6 U+0036
0xF7 U+0037
0xF8 U+0038
0xF9 U+0039
0xFB U+0666
0xFC U+0667
0xFD U+0668
0xFE U+0669
0xFF U+009F

View File

@ -1 +1 @@
0x25 U+000a
0x25 U+000a

View File

@ -26,6 +26,7 @@
package build.tools.charsetmapping;
import java.io.*;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.Formatter;
@ -54,33 +55,19 @@ public class GenerateSBCS {
String pkgName = fields[4];
System.out.printf("%s,%s,%s,%b,%s%n", clzName, csName, hisName, isASCII, pkgName);
StringBuilder b2c = new StringBuilder();
int c2bLen = genB2C(
new FileInputStream(new File(args[0], clzName+".map")), b2c);
String b2cNR = null;
File nrF = new File(args[0], clzName+".nr");
if (nrF.exists()) {
b2cNR = genNR(new FileInputStream(nrF));
}
String c2bNR = null;
File c2bF = new File(args[0], clzName+".c2b");
if (c2bF.exists()) {
c2bNR = genC2BNR(new FileInputStream(c2bF));
}
genSBCSClass(args[0], args[1], "SingleByte-X.java",
clzName, csName, hisName, pkgName, isASCII,
b2c.toString(), b2cNR, c2bNR, c2bLen);
genClass(args[0], args[1], "SingleByte-X.java",
clzName, csName, hisName, pkgName, isASCII);
}
}
private static void toString(char[] sb, int off, int end,
Formatter out, String closure) {
Formatter out, String closure,
boolean comment) {
while (off < end) {
out.format(" \"");
for (int j = 0; j < 8; j++) {
if (off == end)
break;
char c = sb[off++];
switch (c) {
case '\b':
@ -103,101 +90,124 @@ public class GenerateSBCS {
out.format("\\u%04X", c & 0xffff);
}
}
if (off == end)
out.format("\" %s // 0x%02x - 0x%02x%n", closure, off-8, off-1);
else
out.format("\" + // 0x%02x - 0x%02x%n", off-8, off-1);
if (comment) {
if (off == end)
out.format("\" %s // 0x%02x - 0x%02x%n",
closure, off-8, off-1);
else
out.format("\" + // 0x%02x - 0x%02x%n",
off-8, off-1);
} else {
if (off == end)
out.format("\"%s%n", closure);
else
out.format("\" +%n");
}
}
}
static Pattern sbmap = Pattern.compile("0x(\\p{XDigit}++)\\s++U\\+(\\p{XDigit}++)(\\s++#.*)?");
private static int genB2C(InputStream in, StringBuilder out)
private static void genClass(String srcDir, String dstDir,
String template,
String clzName,
String csName,
String hisName,
String pkgName,
boolean isASCII)
throws Exception
{
StringBuilder b2cSB = new StringBuilder();
StringBuilder b2cNRSB = new StringBuilder();
StringBuilder c2bNRSB = new StringBuilder();
char[] sb = new char[0x100];
int[] indexC2B = new int[0x100];
char[] c2bIndex = new char[0x100];
int c2bOff = 0;
Arrays.fill(sb, UNMAPPABLE_DECODING);
Arrays.fill(c2bIndex, UNMAPPABLE_DECODING);
for (int i = 0; i < sb.length; i++)
sb[i] = UNMAPPABLE_DECODING;
// parse the b2c mapping table
// (1)read in .map to parse all b->c entries
FileInputStream in = new FileInputStream(
new File(srcDir, clzName + ".map"));
Parser p = new Parser(in, sbmap);
Entry e = null;
int off = 0;
while ((e = p.next()) != null) {
sb[e.bs] = (char)e.cp;
if (indexC2B[e.cp>>8] == 0) {
off += 0x100;
indexC2B[e.cp>>8] = 1;
if (c2bIndex[e.cp>>8] == UNMAPPABLE_DECODING) {
c2bOff += 0x100;
c2bIndex[e.cp>>8] = 1;
}
}
Formatter fm = new Formatter(out);
Formatter fm = new Formatter(b2cSB);
fm.format("%n");
// vm -server shows cc[byte + 128] access is much faster than
// cc[byte&0xff] so we output the upper segment first
toString(sb, 0x80, 0x100, fm, "+");
toString(sb, 0x00, 0x80, fm, ";");
toString(sb, 0x80, 0x100, fm, "+", true);
toString(sb, 0x00, 0x80, fm, ";", true);
fm.close();
return off;
}
// generate non-roundtrip entries from xxx.nr file
private static String genNR(InputStream in) throws Exception
{
StringBuilder sb = new StringBuilder();
Formatter fm = new Formatter(sb);
Parser p = new Parser(in, sbmap);
Entry e = null;
fm.format("// remove non-roundtrip entries%n");
fm.format(" b2cMap = b2cTable.toCharArray();%n");
while ((e = p.next()) != null) {
fm.format(" b2cMap[%d] = UNMAPPABLE_DECODING;%n",
(e.bs>=0x80)?(e.bs-0x80):(e.bs+0x80));
}
fm.close();
return sb.toString();
}
// (2)now the .nr file which includes "b->c" non-roundtrip entries
File f = new File(srcDir, clzName + ".nr");
if (f.exists()) {
in = new FileInputStream(f);
fm = new Formatter(b2cNRSB);
p = new Parser(in, sbmap);
e = null;
// generate c2b only entries from xxx.c2b file
private static String genC2BNR(InputStream in) throws Exception
{
StringBuilder sb = new StringBuilder();
Formatter fm = new Formatter(sb);
Parser p = new Parser(in, sbmap);
ArrayList<Entry> es = new ArrayList<Entry>();
Entry e = null;
while ((e = p.next()) != null) {
es.add(e);
fm.format("// remove non-roundtrip entries%n");
fm.format(" b2cMap = b2cTable.toCharArray();%n");
while ((e = p.next()) != null) {
fm.format(" b2cMap[%d] = UNMAPPABLE_DECODING;%n",
(e.bs>=0x80)?(e.bs-0x80):(e.bs+0x80));
}
fm.close();
}
fm.format("// non-roundtrip c2b only entries%n");
fm.format(" c2bNR = new char[%d];%n", es.size() * 2);
int i = 0;
for (Entry entry: es) {
fm.format(" c2bNR[%d] = 0x%x; c2bNR[%d] = 0x%x;%n",
i++, entry.bs, i++, entry.cp);
// (3)finally the .c2b file which includes c->b non-roundtrip entries
f = new File(srcDir, clzName + ".c2b");
if (f.exists()) {
in = new FileInputStream(f);
fm = new Formatter(c2bNRSB);
p = new Parser(in, sbmap);
e = null;
ArrayList<Entry> es = new ArrayList<Entry>();
while ((e = p.next()) != null) {
if (c2bIndex[e.cp>>8] == UNMAPPABLE_DECODING) {
c2bOff += 0x100;
c2bIndex[e.cp>>8] = 1;
}
es.add(e);
}
fm.format("// non-roundtrip c2b only entries%n");
if (es.size() < 100) {
fm.format(" c2bNR = new char[%d];%n", es.size() * 2);
int i = 0;
for (Entry entry: es) {
fm.format(" c2bNR[%d] = 0x%x; c2bNR[%d] = 0x%x;%n",
i++, entry.bs, i++, entry.cp);
}
} else {
char[] cc = new char[es.size() * 2];
int i = 0;
for (Entry entry: es) {
cc[i++] = (char)entry.bs;
cc[i++] = (char)entry.cp;
}
fm.format(" c2bNR = (%n");
toString(cc, 0, i, fm, ").toCharArray();", false);
}
fm.close();
}
fm.close();
return sb.toString();
}
private static void genSBCSClass(String srcDir,
String dstDir,
String template,
String clzName,
String csName,
String hisName,
String pkgName,
boolean isASCII,
String b2c,
String b2cNR,
String c2bNR,
int c2blen)
throws Exception
{
// (4)it's time to generate the source file
String b2c = b2cSB.toString();
String b2cNR = b2cNRSB.toString();
String c2bNR = c2bNRSB.toString();
Scanner s = new Scanner(new File(srcDir, template));
PrintStream out = new PrintStream(new FileOutputStream(
new File(dstDir, clzName + ".java")));
@ -239,16 +249,16 @@ public class GenerateSBCS {
line = line.replace("$B2CTABLE$", b2c);
}
if (line.indexOf("$C2BLENGTH$") != -1) {
line = line.replace("$C2BLENGTH$", "0x" + Integer.toString(c2blen, 16));
line = line.replace("$C2BLENGTH$", "0x" + Integer.toString(c2bOff, 16));
}
if (line.indexOf("$NONROUNDTRIP_B2C$") != -1) {
if (b2cNR == null)
if (b2cNR.length() == 0)
continue;
line = line.replace("$NONROUNDTRIP_B2C$", b2cNR);
}
if (line.indexOf("$NONROUNDTRIP_C2B$") != -1) {
if (c2bNR == null)
if (c2bNR.length() == 0)
continue;
line = line.replace("$NONROUNDTRIP_C2B$", c2bNR);
}

View File

@ -48,7 +48,7 @@ public final class BeanInfoFinder
}
private static boolean isValid(Class<?> type, Method method) {
return (method != null) && type.equals(method.getDeclaringClass());
return (method != null) && method.getDeclaringClass().isAssignableFrom(type);
}
@Override

View File

@ -51,7 +51,7 @@ public class BMPImageReaderSpi extends ImageReaderSpi {
entensions,
mimeType,
"com.sun.imageio.plugins.bmp.BMPImageReader",
STANDARD_INPUT_TYPE,
new Class[] { ImageInputStream.class },
writerSpiNames,
false,
null, null, null, null,

View File

@ -32,6 +32,7 @@ import java.awt.image.SinglePixelPackedSampleModel;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.spi.IIORegistry;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.ImageWriter;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.IIOException;
@ -55,7 +56,7 @@ public class BMPImageWriterSpi extends ImageWriterSpi {
entensions,
mimeType,
"com.sun.imageio.plugins.bmp.BMPImageWriter",
STANDARD_OUTPUT_TYPE,
new Class[] { ImageOutputStream.class },
readerSpiNames,
false,
null, null, null, null,

View File

@ -60,7 +60,7 @@ public class GIFImageReaderSpi extends ImageReaderSpi {
suffixes,
MIMETypes,
readerClassName,
STANDARD_INPUT_TYPE,
new Class[] { ImageInputStream.class },
writerSpiNames,
true,
GIFStreamMetadata.nativeMetadataFormatName,

View File

@ -31,6 +31,7 @@ import java.util.Locale;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream;
import com.sun.imageio.plugins.common.PaletteBuilder;
public class GIFImageWriterSpi extends ImageWriterSpi {
@ -59,7 +60,7 @@ public class GIFImageWriterSpi extends ImageWriterSpi {
suffixes,
MIMETypes,
writerClassName,
STANDARD_OUTPUT_TYPE,
new Class[] { ImageOutputStream.class },
readerSpiNames,
true,
GIFWritableStreamMetadata.NATIVE_FORMAT_NAME,

View File

@ -46,7 +46,7 @@ public class JPEGImageReaderSpi extends ImageReaderSpi {
JPEG.suffixes,
JPEG.MIMETypes,
"com.sun.imageio.plugins.jpeg.JPEGImageReader",
STANDARD_INPUT_TYPE,
new Class[] { ImageInputStream.class },
writerSpiNames,
true,
JPEG.nativeStreamMetadataFormatName,

View File

@ -28,6 +28,7 @@ package com.sun.imageio.plugins.jpeg;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.spi.IIORegistry;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.ImageWriter;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.IIOException;
@ -49,7 +50,7 @@ public class JPEGImageWriterSpi extends ImageWriterSpi {
JPEG.suffixes,
JPEG.MIMETypes,
"com.sun.imageio.plugins.jpeg.JPEGImageWriter",
STANDARD_OUTPUT_TYPE,
new Class[] { ImageOutputStream.class },
readerSpiNames,
true,
JPEG.nativeStreamMetadataFormatName,

View File

@ -60,7 +60,7 @@ public class PNGImageReaderSpi extends ImageReaderSpi {
suffixes,
MIMETypes,
readerClassName,
STANDARD_INPUT_TYPE,
new Class[] { ImageInputStream.class },
writerSpiNames,
false,
null, null,

View File

@ -34,6 +34,7 @@ import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadataFormat;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream;
public class PNGImageWriterSpi extends ImageWriterSpi {
@ -61,7 +62,7 @@ public class PNGImageWriterSpi extends ImageWriterSpi {
suffixes,
MIMETypes,
writerClassName,
STANDARD_OUTPUT_TYPE,
new Class[] { ImageOutputStream.class },
readerSpiNames,
false,
null, null,

View File

@ -55,7 +55,7 @@ public class WBMPImageReaderSpi extends ImageReaderSpi {
entensions,
mimeType,
"com.sun.imageio.plugins.wbmp.WBMPImageReader",
STANDARD_INPUT_TYPE,
new Class[] { ImageInputStream.class },
writerSpiNames,
true,
null, null, null, null,

View File

@ -28,6 +28,7 @@ package com.sun.imageio.plugins.wbmp;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.spi.IIORegistry;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.ImageWriter;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.IIOException;
@ -54,7 +55,7 @@ public class WBMPImageWriterSpi extends ImageWriterSpi {
entensions,
mimeType,
"com.sun.imageio.plugins.wbmp.WBMPImageWriter",
STANDARD_OUTPUT_TYPE,
new Class[] { ImageOutputStream.class },
readerSpiNames,
true,
null, null, null, null,

View File

@ -43,35 +43,35 @@ import javax.imageio.stream.ImageInputStream;
*/
public class StreamCloser {
private static WeakHashMap<ImageInputStream, Object> toCloseQueue;
private static WeakHashMap<CloseAction, Object> toCloseQueue;
private static Thread streamCloser;
public static void addToQueue(ImageInputStream iis) {
public static void addToQueue(CloseAction ca) {
synchronized (StreamCloser.class) {
if (toCloseQueue == null) {
toCloseQueue =
new WeakHashMap<ImageInputStream, Object>();
new WeakHashMap<CloseAction, Object>();
}
toCloseQueue.put(iis, null);
toCloseQueue.put(ca, null);
if (streamCloser == null) {
final Runnable streamCloserRunnable = new Runnable() {
public void run() {
if (toCloseQueue != null) {
synchronized (StreamCloser.class) {
Set<ImageInputStream> set =
Set<CloseAction> set =
toCloseQueue.keySet();
// Make a copy of the set in order to avoid
// concurrent modification (the is.close()
// will in turn call removeFromQueue())
ImageInputStream[] streams =
new ImageInputStream[set.size()];
streams = set.toArray(streams);
for (ImageInputStream is : streams) {
if (is != null) {
CloseAction[] actions =
new CloseAction[set.size()];
actions = set.toArray(actions);
for (CloseAction ca : actions) {
if (ca != null) {
try {
is.close();
ca.performAction();
} catch (IOException e) {
}
}
@ -106,10 +106,28 @@ public class StreamCloser {
}
}
public static void removeFromQueue(ImageInputStream iis) {
public static void removeFromQueue(CloseAction ca) {
synchronized (StreamCloser.class) {
if (toCloseQueue != null) {
toCloseQueue.remove(iis);
toCloseQueue.remove(ca);
}
}
}
public static CloseAction createCloseAction(ImageInputStream iis) {
return new CloseAction(iis);
}
public static final class CloseAction {
private ImageInputStream iis;
private CloseAction(ImageInputStream iis) {
this.iis = iis;
}
public void performAction() throws IOException {
if (iis != null) {
iis.close();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -922,7 +922,7 @@ public class DnsContext extends ComponentDirContext {
//---------- Debugging
public static boolean debug = false;
private static final boolean debug = false;
private static final void dprint(String msg) {
if (debug) {
@ -972,14 +972,11 @@ class NameClassPairEnumeration implements NamingEnumeration {
}
/*
* ctx will be closed when no longer needed by the enumeration.
* ctx will be set to null when no longer needed by the enumeration.
*/
public void close () {
public void close() {
nodes = null;
if (ctx != null) {
ctx.close();
ctx = null;
}
ctx = null;
}
public boolean hasMore() {

View File

@ -1,5 +1,5 @@
/*
* Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -41,6 +41,15 @@ import javax.sound.midi.spi.MidiFileWriter;
import javax.sound.midi.spi.SoundbankReader;
import javax.sound.midi.spi.MidiDeviceProvider;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
import javax.sound.midi.Transmitter;
import javax.sound.sampled.Clip;
import javax.sound.sampled.Port;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
/**
* JDK13Services uses the Service class in JDK 1.3
@ -186,6 +195,16 @@ public class JDK13Services {
If the property is not set, null is returned.
*/
private static synchronized String getDefaultProvider(Class typeClass) {
if (!SourceDataLine.class.equals(typeClass)
&& !TargetDataLine.class.equals(typeClass)
&& !Clip.class.equals(typeClass)
&& !Port.class.equals(typeClass)
&& !Receiver.class.equals(typeClass)
&& !Transmitter.class.equals(typeClass)
&& !Synthesizer.class.equals(typeClass)
&& !Sequencer.class.equals(typeClass)) {
return null;
}
String value;
String propertyName = typeClass.getName();
value = JSSecurityManager.getProperty(propertyName);

View File

@ -1,5 +1,5 @@
/*
* Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -283,28 +283,37 @@ class JSSecurityManager {
static List getProviders(final Class providerClass) {
PrivilegedAction action = new PrivilegedAction() {
public Object run() {
List p = new ArrayList();
Iterator ps = Service.providers(providerClass);
while (ps.hasNext()) {
try {
Object provider = ps.next();
if (providerClass.isInstance(provider)) {
// $$mp 2003-08-22
// Always adding at the beginning reverses the
// order of the providers. So we no longer have
// to do this in AudioSystem and MidiSystem.
p.add(0, provider);
}
} catch (Throwable t) {
//$$fb 2002-11-07: do not fail on SPI not found
if (Printer.err) t.printStackTrace();
} }
return p;
List p = new ArrayList();
// Service.providers(Class) just creates "lazy" iterator instance,
// so it doesn't require do be called from privileged section
final Iterator ps = Service.providers(providerClass);
// the iterator's hasNext() method looks through classpath for
// the provider class names, so it requires read permissions
PrivilegedAction<Boolean> hasNextAction = new PrivilegedAction<Boolean>() {
public Boolean run() {
return ps.hasNext();
}
};
while (AccessController.doPrivileged(hasNextAction)) {
try {
// the iterator's next() method creates instances of the
// providers and it should be called in the current security
// context
Object provider = ps.next();
if (providerClass.isInstance(provider)) {
// $$mp 2003-08-22
// Always adding at the beginning reverses the
// order of the providers. So we no longer have
// to do this in AudioSystem and MidiSystem.
p.add(0, provider);
}
};
List providers = (List) AccessController.doPrivileged(action);
return providers;
} catch (Throwable t) {
//$$fb 2002-11-07: do not fail on SPI not found
if (Printer.err) t.printStackTrace();
}
}
return p;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -82,7 +82,7 @@ public class StandardMidiFileWriter extends MidiFileWriter {
/**
* MIDI parser types
*/
public static final int types[] = {
private static final int types[] = {
MIDI_TYPE_0,
MIDI_TYPE_1
};

View File

@ -60,8 +60,14 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
*/
public abstract String engineGetURI();
/**
* Returns the output length of the hash/digest.
*/
abstract int getDigestLength();
/** Field _macAlgorithm */
private Mac _macAlgorithm = null;
private boolean _HMACOutputLengthSet = false;
/** Field _HMACOutputLength */
int _HMACOutputLength = 0;
@ -100,7 +106,9 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
}
public void reset() {
_HMACOutputLength=0;
_HMACOutputLength=0;
_HMACOutputLengthSet = false;
_macAlgorithm.reset();
}
/**
@ -115,14 +123,16 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
throws XMLSignatureException {
try {
byte[] completeResult = this._macAlgorithm.doFinal();
if ((this._HMACOutputLength == 0) || (this._HMACOutputLength >= 160)) {
if (this._HMACOutputLengthSet && this._HMACOutputLength < getDigestLength()) {
if (log.isLoggable(java.util.logging.Level.FINE)) {
log.log(java.util.logging.Level.FINE,
"HMACOutputLength must not be less than " + getDigestLength());
}
throw new XMLSignatureException("errorMessages.XMLSignatureException");
} else {
byte[] completeResult = this._macAlgorithm.doFinal();
return MessageDigestAlgorithm.isEqual(completeResult, signature);
}
byte[] stripped = IntegrityHmac.reduceBitLength(completeResult,
this._HMACOutputLength);
return MessageDigestAlgorithm.isEqual(stripped, signature);
} catch (IllegalStateException ex) {
throw new XMLSignatureException("empty", ex);
}
@ -176,14 +186,15 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
protected byte[] engineSign() throws XMLSignatureException {
try {
byte[] completeResult = this._macAlgorithm.doFinal();
if ((this._HMACOutputLength == 0) || (this._HMACOutputLength >= 160)) {
return completeResult;
if (this._HMACOutputLengthSet && this._HMACOutputLength < getDigestLength()) {
if (log.isLoggable(java.util.logging.Level.FINE)) {
log.log(java.util.logging.Level.FINE,
"HMACOutputLength must not be less than " + getDigestLength());
}
throw new XMLSignatureException("errorMessages.XMLSignatureException");
} else {
return this._macAlgorithm.doFinal();
}
return IntegrityHmac.reduceBitLength(completeResult,
this._HMACOutputLength);
} catch (IllegalStateException ex) {
throw new XMLSignatureException("empty", ex);
}
@ -361,6 +372,7 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
*/
protected void engineSetHMACOutputLength(int HMACOutputLength) {
this._HMACOutputLength = HMACOutputLength;
this._HMACOutputLengthSet = true;
}
/**
@ -376,12 +388,13 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
throw new IllegalArgumentException("element null");
}
Text hmaclength =XMLUtils.selectDsNodeText(element.getFirstChild(),
Constants._TAG_HMACOUTPUTLENGTH,0);
Text hmaclength =XMLUtils.selectDsNodeText(element.getFirstChild(),
Constants._TAG_HMACOUTPUTLENGTH,0);
if (hmaclength != null) {
this._HMACOutputLength = Integer.parseInt(hmaclength.getData());
}
if (hmaclength != null) {
this._HMACOutputLength = Integer.parseInt(hmaclength.getData());
this._HMACOutputLengthSet = true;
}
}
@ -390,14 +403,13 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
*
* @param element
*/
public void engineAddContextToElement(Element element)
{
public void engineAddContextToElement(Element element) {
if (element == null) {
throw new IllegalArgumentException("null element");
}
if (this._HMACOutputLength != 0) {
if (this._HMACOutputLengthSet) {
Document doc = element.getOwnerDocument();
Element HMElem = XMLUtils.createElementInSignatureSpace(doc,
Constants._TAG_HMACOUTPUTLENGTH);
@ -436,6 +448,10 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
public String engineGetURI() {
return XMLSignature.ALGO_ID_MAC_HMAC_SHA1;
}
int getDigestLength() {
return 160;
}
}
/**
@ -463,6 +479,10 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
public String engineGetURI() {
return XMLSignature.ALGO_ID_MAC_HMAC_SHA256;
}
int getDigestLength() {
return 256;
}
}
/**
@ -490,6 +510,10 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
public String engineGetURI() {
return XMLSignature.ALGO_ID_MAC_HMAC_SHA384;
}
int getDigestLength() {
return 384;
}
}
/**
@ -517,6 +541,10 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
public String engineGetURI() {
return XMLSignature.ALGO_ID_MAC_HMAC_SHA512;
}
int getDigestLength() {
return 512;
}
}
/**
@ -544,6 +572,10 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
public String engineGetURI() {
return XMLSignature.ALGO_ID_MAC_HMAC_RIPEMD160;
}
int getDigestLength() {
return 160;
}
}
/**
@ -571,5 +603,9 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi {
public String engineGetURI() {
return XMLSignature.ALGO_ID_MAC_HMAC_NOT_RECOMMENDED_MD5;
}
int getDigestLength() {
return 128;
}
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.security.jgss;
/**
* Kerberos 5 AuthorizationData entry.
*/
final public class AuthorizationDataEntry {
private final int type;
private final byte[] data;
/**
* Create an AuthorizationDataEntry object.
* @param type the ad-type
* @param data the ad-data, a copy of the data will be saved
* inside the object.
*/
public AuthorizationDataEntry(int type, byte[] data) {
this.type = type;
this.data = data.clone();
}
/**
* Get the ad-type field.
* @return ad-type
*/
public int getType() {
return type;
}
/**
* Get a copy of the ad-data field.
* @return ad-data
*/
public byte[] getData() {
return data.clone();
}
public String toString() {
return "AuthorizationDataEntry: type="+type+", data=" +
data.length + " bytes:\n" +
new sun.misc.HexDumpEncoder().encode(data);
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.security.jgss;
import org.ietf.jgss.*;
/**
* The extended GSSContext interface for supporting additional
* functionalities not defined by {@code org.ietf.jgss.GSSContext},
* such as querying context-specific attributes.
*/
public interface ExtendedGSSContext extends GSSContext {
/**
* Return the mechanism-specific attribute associated with {@code type}.
* <br><br>
* For each supported attribute type, the type for the output are
* defined below.
* <ol>
* <li>{@code KRB5_GET_TKT_FLAGS}:
* the returned object is a boolean array for the service ticket flags,
* which is long enough to contain all true bits. This means if
* the user wants to get the <em>n</em>'th bit but the length of the
* returned array is less than <em>n</em>, it is regarded as false.
* <li>{@code KRB5_GET_SESSION_KEY}:
* the returned object is an instance of {@link java.security.Key},
* which has the following properties:
* <ul>
* <li>Algorithm: enctype as a string, where
* enctype is defined in RFC 3961, section 8.
* <li>Format: "RAW"
* <li>Encoded form: the raw key bytes, not in any ASN.1 encoding
* </ul>
* <li>{@code KRB5_GET_AUTHZ_DATA}:
* the returned object is an array of
* {@link com.sun.security.jgss.AuthorizationDataEntry}, or null if the
* optional field is missing in the service ticket.
* <li>{@code KRB5_GET_AUTHTIME}:
* the returned object is a String object in the standard KerberosTime
* format defined in RFC 4120 5.2.3
* </ol>
*
* If there is a security manager, an {@link InquireSecContextPermission}
* with the name {@code type.mech} must be granted. Otherwise, this could
* result in a {@link SecurityException}.<p>
*
* Example:
* <pre>
* GSSContext ctxt = m.createContext(...)
* // Establishing the context
* if (ctxt instanceof ExtendedGSSContext) {
* ExtendedGSSContext ex = (ExtendedGSSContext)ctxt;
* try {
* Key key = (key)ex.inquireSecContext(
* InquireType.KRB5_GET_SESSION_KEY);
* // read key info
* } catch (GSSException gsse) {
* // deal with exception
* }
* }
* </pre>
* @param type the type of the attribute requested
* @return the attribute, see the method documentation for details.
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_MECH GSSException.BAD_MECH} if the mechanism
* does not support this method,
* {@link GSSException#UNAVAILABLE GSSException.UNAVAILABLE} if the
* type specified is not supported,
* {@link GSSException#NO_CONTEXT GSSException.NO_CONTEXT} if the
* security context is invalid,
* {@link GSSException#FAILURE GSSException.FAILURE} for other
* unspecified failures.
* @throws SecurityException if a security manager exists and a proper
* {@link InquireSecContextPermission} is not granted.
* @see InquireSecContextPermission
*/
public Object inquireSecContext(InquireType type)
throws GSSException;
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.security.jgss;
import java.security.BasicPermission;
/**
* This class is used to protect various attributes of an established
* GSS security context that can be accessed using the
* {@link com.sun.security.jgss.ExtendedGSSContext#inquireSecContext}
* method.
*
* <p>The target name is the {@link InquireType} allowed.
*/
public final class InquireSecContextPermission extends BasicPermission {
/**
* Constructs a new {@code InquireSecContextPermission} object with
* the specified name. The name is the symbolic name of the
* {@link InquireType} allowed.
*
* @param name the {@link InquireType} allowed by this
* permission. "*" means all {@link InquireType}s are allowed.
*
* @throws NullPointerException if <code>name</code> is <code>null</code>.
* @throws IllegalArgumentException if <code>name</code> is empty.
*/
public InquireSecContextPermission(String name) {
super(name);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.security.jgss;
/**
* Attribute types that can be specified as an argument of
* {@link com.sun.security.jgss.ExtendedGSSContext#inquireSecContext}
*/
public enum InquireType {
/**
* Attribute type for retrieving the session key of an
* established Kerberos 5 security context.
*/
KRB5_GET_SESSION_KEY,
/**
* Attribute type for retrieving the service ticket flags of an
* established Kerberos 5 security context.
*/
KRB5_GET_TKT_FLAGS,
/**
* Attribute type for retrieving the authorization data in the
* service ticket of an established Kerberos 5 security context.
* Only supported on the acceptor side.
*/
KRB5_GET_AUTHZ_DATA,
/**
* Attribute type for retrieving the authtime in the service ticket
* of an established Kerberos 5 security context.
*/
KRB5_GET_AUTHTIME
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -48,10 +48,6 @@ import sun.misc.HexDumpEncoder;
* @author Rosanna Lee
*/
public abstract class AbstractSaslImpl {
/**
* Logger for debug messages
*/
protected static Logger logger; // set in initLogger(); lazily loads logger
protected boolean completed = false;
protected boolean privacy = false;
@ -68,7 +64,6 @@ public abstract class AbstractSaslImpl {
protected String myClassName;
protected AbstractSaslImpl(Map props, String className) throws SaslException {
initLogger();
myClassName = className;
// Parse properties to set desired context options
@ -325,19 +320,15 @@ public abstract class AbstractSaslImpl {
}
}
/**
* Sets logger field.
*/
private static synchronized void initLogger() {
if (logger == null) {
logger = Logger.getLogger(SASL_LOGGER_NAME);
}
}
// ---------------- Constants -----------------
private static final String SASL_LOGGER_NAME = "javax.security.sasl";
protected static final String MAX_SEND_BUF = "javax.security.sasl.sendmaxbuffer";
/**
* Logger for debug messages
*/
protected static final Logger logger = Logger.getLogger(SASL_LOGGER_NAME);
// default 0 (no protection); 1 (integrity only)
protected static final byte NO_PROTECTION = (byte)1;
protected static final byte INTEGRITY_ONLY_PROTECTION = (byte)2;

View File

@ -118,8 +118,18 @@ public class Cursor implements java.io.Serializable {
*/
public static final int MOVE_CURSOR = 13;
/**
* @deprecated As of JDK version 1.7, the {@link #getPredefinedCursor()}
* method should be used instead.
*/
@Deprecated
protected static Cursor predefined[] = new Cursor[14];
/**
* This field is a private replacement for 'predefined' array.
*/
private final static Cursor[] predefinedPrivate = new Cursor[14];
/* Localization names and default values */
static final String[][] cursorProperties = {
{ "AWT.DefaultCursor", "Default Cursor" },
@ -253,10 +263,15 @@ public class Cursor implements java.io.Serializable {
if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {
throw new IllegalArgumentException("illegal cursor type");
}
if (predefined[type] == null) {
predefined[type] = new Cursor(type);
Cursor c = predefinedPrivate[type];
if (c == null) {
predefinedPrivate[type] = c = new Cursor(type);
}
return predefined[type];
// fill 'predefined' array for backwards compatibility.
if (predefined[type] == null) {
predefined[type] = c;
}
return c;
}
/**

View File

@ -3743,16 +3743,58 @@ public class Window extends Container implements Accessible {
// ****************** END OF MIXING CODE ********************************
// This method gets the window location/size as reported by the native
// system since the locally cached values may represent outdated data.
// NOTE: this method is invoked on the toolkit thread, and therefore
// is not supposed to become public/user-overridable.
/**
* Limit the given double value with the given range.
*/
private static double limit(double value, double min, double max) {
value = Math.max(value, min);
value = Math.min(value, max);
return value;
}
/**
* Calculate the position of the security warning.
*
* This method gets the window location/size as reported by the native
* system since the locally cached values may represent outdated data.
*
* The method is used from the native code, or via AWTAccessor.
*
* NOTE: this method is invoked on the toolkit thread, and therefore is not
* supposed to become public/user-overridable.
*/
private Point2D calculateSecurityWarningPosition(double x, double y,
double w, double h)
{
return new Point2D.Double(
x + w * securityWarningAlignmentX + securityWarningPointX,
y + h * securityWarningAlignmentY + securityWarningPointY);
// The position according to the spec of SecurityWarning.setPosition()
double wx = x + w * securityWarningAlignmentX + securityWarningPointX;
double wy = y + h * securityWarningAlignmentY + securityWarningPointY;
// First, make sure the warning is not too far from the window bounds
wx = Window.limit(wx,
x - securityWarningWidth - 2,
x + w + 2);
wy = Window.limit(wy,
y - securityWarningHeight - 2,
y + h + 2);
// Now make sure the warning window is visible on the screen
GraphicsConfiguration graphicsConfig =
getGraphicsConfiguration_NoClientCode();
Rectangle screenBounds = graphicsConfig.getBounds();
Insets screenInsets =
Toolkit.getDefaultToolkit().getScreenInsets(graphicsConfig);
wx = Window.limit(wx,
screenBounds.x + screenInsets.left,
screenBounds.x + screenBounds.width - screenInsets.right
- securityWarningWidth);
wy = Window.limit(wy,
screenBounds.y + screenInsets.top,
screenBounds.y + screenBounds.height - screenInsets.bottom
- securityWarningHeight);
return new Point2D.Double(wx, wy);
}
static {

View File

@ -114,8 +114,8 @@ public class Introspector {
// Static Caches to speed up introspection.
private static Map declaredMethodCache =
Collections.synchronizedMap(new WeakHashMap());
private static Map beanInfoCache =
Collections.synchronizedMap(new WeakHashMap());
private static final Object BEANINFO_CACHE = new Object();
private Class beanClass;
private BeanInfo explicitBeanInfo;
@ -174,10 +174,18 @@ public class Introspector {
if (!ReflectUtil.isPackageAccessible(beanClass)) {
return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
}
BeanInfo bi = (BeanInfo)beanInfoCache.get(beanClass);
Map<Class<?>, BeanInfo> map;
synchronized (BEANINFO_CACHE) {
map = (Map<Class<?>, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE);
if (map == null) {
map = Collections.synchronizedMap(new WeakHashMap<Class<?>, BeanInfo>());
AppContext.getAppContext().put(BEANINFO_CACHE, map);
}
}
BeanInfo bi = map.get(beanClass);
if (bi == null) {
bi = (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
beanInfoCache.put(beanClass, bi);
map.put(beanClass, bi);
}
return bi;
}
@ -351,7 +359,10 @@ public class Introspector {
*/
public static void flushCaches() {
beanInfoCache.clear();
Map map = (Map) AppContext.getAppContext().get(BEANINFO_CACHE);
if (map != null) {
map.clear();
}
declaredMethodCache.clear();
}
@ -374,7 +385,10 @@ public class Introspector {
if (clz == null) {
throw new NullPointerException();
}
beanInfoCache.remove(clz);
Map map = (Map) AppContext.getAppContext().get(BEANINFO_CACHE);
if (map != null) {
map.remove(clz);
}
declaredMethodCache.remove(clz);
}

View File

@ -335,31 +335,6 @@ abstract class java_util_Collections extends PersistenceDelegate {
return (oldC.size() == newC.size()) && oldC.containsAll(newC);
}
static Object getPrivateField(final Object instance, final String name) {
return AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
Class type = instance.getClass();
while ( true ) {
try {
Field field = type.getDeclaredField(name);
field.setAccessible(true);
return field.get( instance );
}
catch (NoSuchFieldException exception) {
type = type.getSuperclass();
if (type == null) {
throw new IllegalStateException("Could not find field " + name, exception);
}
}
catch (Exception exception) {
throw new IllegalStateException("Could not get value " + type.getName() + '.' + name, exception);
}
}
}
} );
}
static final class EmptyList_PersistenceDelegate extends java_util_Collections {
protected Expression instantiate(Object oldInstance, Encoder out) {
return new Expression(oldInstance, Collections.class, "emptyList", null);
@ -500,7 +475,7 @@ abstract class java_util_Collections extends PersistenceDelegate {
static final class CheckedCollection_PersistenceDelegate extends java_util_Collections {
protected Expression instantiate(Object oldInstance, Encoder out) {
Object type = getPrivateField(oldInstance, "type");
Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type");
List list = new ArrayList((Collection) oldInstance);
return new Expression(oldInstance, Collections.class, "checkedCollection", new Object[]{list, type});
}
@ -508,7 +483,7 @@ abstract class java_util_Collections extends PersistenceDelegate {
static final class CheckedList_PersistenceDelegate extends java_util_Collections {
protected Expression instantiate(Object oldInstance, Encoder out) {
Object type = getPrivateField(oldInstance, "type");
Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type");
List list = new LinkedList((Collection) oldInstance);
return new Expression(oldInstance, Collections.class, "checkedList", new Object[]{list, type});
}
@ -516,7 +491,7 @@ abstract class java_util_Collections extends PersistenceDelegate {
static final class CheckedRandomAccessList_PersistenceDelegate extends java_util_Collections {
protected Expression instantiate(Object oldInstance, Encoder out) {
Object type = getPrivateField(oldInstance, "type");
Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type");
List list = new ArrayList((Collection) oldInstance);
return new Expression(oldInstance, Collections.class, "checkedList", new Object[]{list, type});
}
@ -524,7 +499,7 @@ abstract class java_util_Collections extends PersistenceDelegate {
static final class CheckedSet_PersistenceDelegate extends java_util_Collections {
protected Expression instantiate(Object oldInstance, Encoder out) {
Object type = getPrivateField(oldInstance, "type");
Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type");
Set set = new HashSet((Set) oldInstance);
return new Expression(oldInstance, Collections.class, "checkedSet", new Object[]{set, type});
}
@ -532,7 +507,7 @@ abstract class java_util_Collections extends PersistenceDelegate {
static final class CheckedSortedSet_PersistenceDelegate extends java_util_Collections {
protected Expression instantiate(Object oldInstance, Encoder out) {
Object type = getPrivateField(oldInstance, "type");
Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type");
SortedSet set = new TreeSet((SortedSet) oldInstance);
return new Expression(oldInstance, Collections.class, "checkedSortedSet", new Object[]{set, type});
}
@ -540,8 +515,8 @@ abstract class java_util_Collections extends PersistenceDelegate {
static final class CheckedMap_PersistenceDelegate extends java_util_Collections {
protected Expression instantiate(Object oldInstance, Encoder out) {
Object keyType = getPrivateField(oldInstance, "keyType");
Object valueType = getPrivateField(oldInstance, "valueType");
Object keyType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.keyType");
Object valueType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.valueType");
Map map = new HashMap((Map) oldInstance);
return new Expression(oldInstance, Collections.class, "checkedMap", new Object[]{map, keyType, valueType});
}
@ -549,8 +524,8 @@ abstract class java_util_Collections extends PersistenceDelegate {
static final class CheckedSortedMap_PersistenceDelegate extends java_util_Collections {
protected Expression instantiate(Object oldInstance, Encoder out) {
Object keyType = getPrivateField(oldInstance, "keyType");
Object valueType = getPrivateField(oldInstance, "valueType");
Object keyType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.keyType");
Object valueType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.valueType");
SortedMap map = new TreeMap((SortedMap) oldInstance);
return new Expression(oldInstance, Collections.class, "checkedSortedMap", new Object[]{map, keyType, valueType});
}
@ -572,7 +547,7 @@ class java_util_EnumMap_PersistenceDelegate extends PersistenceDelegate {
}
private static Object getType(Object instance) {
return java_util_Collections.getPrivateField(instance, "keyType");
return MetaData.getPrivateFieldValue(instance, "java.util.EnumMap.keyType");
}
}
@ -591,7 +566,7 @@ class java_util_EnumSet_PersistenceDelegate extends PersistenceDelegate {
}
private static Object getType(Object instance) {
return java_util_Collections.getPrivateField(instance, "elementType");
return MetaData.getPrivateFieldValue(instance, "java.util.EnumSet.elementType");
}
}
@ -1282,7 +1257,7 @@ class javax_swing_Box_PersistenceDelegate extends DefaultPersistenceDelegate {
private Integer getAxis(Object object) {
Box box = (Box) object;
return (Integer) java_util_Collections.getPrivateField(box.getLayout(), "axis");
return (Integer) MetaData.getPrivateFieldValue(box.getLayout(), "javax.swing.BoxLayout.axis");
}
}
@ -1365,6 +1340,7 @@ final class sun_swing_PrintColorUIResource_PersistenceDelegate extends Persisten
}
class MetaData {
private static final Map<String,Field> fields = Collections.synchronizedMap(new WeakHashMap<String, Field>());
private static Hashtable internalPersistenceDelegates = new Hashtable();
private static PersistenceDelegate nullPersistenceDelegate = new NullPersistenceDelegate();
@ -1503,4 +1479,35 @@ class MetaData {
return null;
}
}
static Object getPrivateFieldValue(Object instance, String name) {
Field field = fields.get(name);
if (field == null) {
int index = name.lastIndexOf('.');
final String className = name.substring(0, index);
final String fieldName = name.substring(1 + index);
field = AccessController.doPrivileged(new PrivilegedAction<Field>() {
public Field run() {
try {
Field field = Class.forName(className).getDeclaredField(fieldName);
field.setAccessible(true);
return field;
}
catch (ClassNotFoundException exception) {
throw new IllegalStateException("Could not find class", exception);
}
catch (NoSuchFieldException exception) {
throw new IllegalStateException("Could not find field", exception);
}
}
});
fields.put(name, field);
}
try {
return field.get(instance);
}
catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not get value of the field", exception);
}
}
}

View File

@ -38,7 +38,7 @@ import java.util.Locale;
* a character's category (lowercase letter, digit, etc.) and for converting
* characters from uppercase to lowercase and vice versa.
* <p>
* Character information is based on the Unicode Standard, version 4.0.
* Character information is based on the Unicode Standard, version 5.1.0.
* <p>
* The methods and data of class <code>Character</code> are defined by
* the information in the <i>UnicodeData</i> file that is part of the

View File

@ -81,7 +81,7 @@ public interface Instrumentation {
* an exception during execution, the JVM will still call the other registered
* transformers in order. The same transformer may be added more than once,
* but it is strongly discouraged -- avoid this by creating a new instance of
* tranformer class.
* transformer class.
* <P>
* This method is intended for use in instrumentation, as described in the
* {@linkplain Instrumentation class specification}.
@ -183,7 +183,7 @@ public interface Instrumentation {
* <P>
*
* The order of transformation is described in the
* ({@link java.lang.instrument.ClassFileTransformer#transform transform} method.
* {@link java.lang.instrument.ClassFileTransformer#transform transform} method.
* This same order is used in the automatic reapplication of retransformation
* incapable transforms.
* <P>
@ -424,7 +424,7 @@ public interface Instrumentation {
* classes or resources other than those to be defined by the bootstrap
* class loader for the purpose of instrumentation.
* Failure to observe this warning could result in unexpected
* behaviour that is difficult to diagnose. For example, suppose there is a
* behavior that is difficult to diagnose. For example, suppose there is a
* loader L, and L's parent for delegation is the bootstrap class loader.
* Furthermore, a method in class C, a class defined by L, makes reference to
* a non-public accessor class C$1. If the JAR file contains a class C$1 then
@ -475,9 +475,9 @@ public interface Instrumentation {
* classes or resources other than those to be defined by the system class
* loader for the purpose of instrumentation.
* Failure to observe this warning could result in unexpected
* behaviour that is difficult to diagnose (see
* behavior that is difficult to diagnose (see
* {@link #appendToBootstrapClassLoaderSearch
* appendToBootstrapClassLoaderSearch}.
* appendToBootstrapClassLoaderSearch}).
*
* <p> The system class loader supports adding a JAR file to be searched if
* it implements a method named <code>appendToClassPathForInstrumentation</code>
@ -485,7 +485,7 @@ public interface Instrumentation {
* method is not required to have <code>public</code> access. The name of
* the JAR file is obtained by invoking the {@link java.util.zip.ZipFile#getName
* getName()} method on the <code>jarfile</code> and this is provided as the
* parameter to the <code>appendtoClassPathForInstrumentation</code> method.
* parameter to the <code>appendToClassPathForInstrumentation</code> method.
*
* <p> The <a href="http://java.sun.com/docs/books/vmspec/">Java Virtual Machine
* Specification</a> specifies that a subsequent attempt to resolve a symbolic

View File

@ -114,9 +114,14 @@ class Socket implements java.io.Closeable {
* @since 1.5
*/
public Socket(Proxy proxy) {
if (proxy != null && proxy.type() == Proxy.Type.SOCKS) {
// Create a copy of Proxy as a security measure
if (proxy == null) {
throw new IllegalArgumentException("Invalid Proxy");
}
Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY : sun.net.ApplicationProxy.create(proxy);
if (p.type() == Proxy.Type.SOCKS) {
SecurityManager security = System.getSecurityManager();
InetSocketAddress epoint = (InetSocketAddress) proxy.address();
InetSocketAddress epoint = (InetSocketAddress) p.address();
if (security != null) {
if (epoint.isUnresolved())
security.checkConnect(epoint.getHostName(),
@ -125,10 +130,10 @@ class Socket implements java.io.Closeable {
security.checkConnect(epoint.getAddress().getHostAddress(),
epoint.getPort());
}
impl = new SocksSocketImpl(proxy);
impl = new SocksSocketImpl(p);
impl.setSocket(this);
} else {
if (proxy == Proxy.NO_PROXY) {
if (p == Proxy.NO_PROXY) {
if (factory == null) {
impl = new PlainSocketImpl();
impl.setSocket(this);

View File

@ -46,6 +46,9 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
private Socket cmdsock = null;
private InputStream cmdIn = null;
private OutputStream cmdOut = null;
/* true if the Proxy has been set programatically */
private boolean applicationSetProxy; /* false */
SocksSocketImpl() {
// Nothing needed
@ -237,8 +240,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
out.write((endpoint.getPort() >> 8) & 0xff);
out.write((endpoint.getPort() >> 0) & 0xff);
out.write(endpoint.getAddress().getAddress());
String userName = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("user.name"));
String userName = getUserName();
try {
out.write(userName.getBytes("ISO-8859-1"));
} catch (java.io.UnsupportedEncodingException uee) {
@ -554,8 +556,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
out.write((super.getLocalPort() >> 8) & 0xff);
out.write((super.getLocalPort() >> 0) & 0xff);
out.write(addr1);
String userName = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("user.name"));
String userName = getUserName();
try {
out.write(userName.getBytes("ISO-8859-1"));
} catch (java.io.UnsupportedEncodingException uee) {
@ -1022,4 +1023,16 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
super.close();
}
private String getUserName() {
String userName = "";
if (applicationSetProxy) {
try {
userName = System.getProperty("user.name");
} catch (SecurityException se) { /* swallow Exception */ }
} else {
userName = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("user.name"));
}
return userName;
}
}

View File

@ -1004,16 +1004,18 @@ public final class URL implements java.io.Serializable {
throw new IllegalArgumentException("proxy can not be null");
}
// Create a copy of Proxy as a security measure
Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY : sun.net.ApplicationProxy.create(proxy);
SecurityManager sm = System.getSecurityManager();
if (proxy.type() != Proxy.Type.DIRECT && sm != null) {
InetSocketAddress epoint = (InetSocketAddress) proxy.address();
if (p.type() != Proxy.Type.DIRECT && sm != null) {
InetSocketAddress epoint = (InetSocketAddress) p.address();
if (epoint.isUnresolved())
sm.checkConnect(epoint.getHostName(), epoint.getPort());
else
sm.checkConnect(epoint.getAddress().getHostAddress(),
epoint.getPort());
}
return handler.openConnection(this, proxy);
return handler.openConnection(this, p);
}
/**

View File

@ -421,7 +421,7 @@ public abstract class DatagramChannel
* invocation of this method will block until the first operation is
* complete. If this channel's socket is not bound then this method will
* first cause the socket to be bound to an address that is assigned
* automatically, as if by invoking the {@link #bind bind) method with a
* automatically, as if by invoking the {@link #bind bind} method with a
* parameter of {@code null}. </p>
*
* @param src

View File

@ -115,8 +115,8 @@
* <td>Reads, writes, maps, and manipulates files</td></tr>
* <tr><td valign=top><tt>{@link java.nio.channels.FileLock}</tt></td>
* <td>A lock on a (region of a) file</td></tr>
* <tr><td valign=top><tt>{@link java.nio.MappedByteBuffer}/{@link java.nio.MappedBigByteBuffer}&nbsp;&nbsp;</tt></td>
* <td>A direct byte buffer or big byte buffer mapped to a region of a&nbsp;file</td></tr>
* <tr><td valign=top><tt>{@link java.nio.MappedByteBuffer}&nbsp;&nbsp;</tt></td>
* <td>A direct byte buffer mapped to a region of a&nbsp;file</td></tr>
* </table></blockquote>
*
* <p> The {@link java.nio.channels.FileChannel} class supports the usual

View File

@ -53,7 +53,7 @@ import java.io.IOException;
* invoking the {@link #close close} method. Closing the directory stream
* releases any resources associated with the stream. Once a directory stream
* is closed, all further method invocations on the iterator throw {@link
* java.util.concurrent.ConcurrentModificationException} with cause {@link
* java.util.ConcurrentModificationException} with cause {@link
* ClosedDirectoryStreamException}.
*
* <p> A directory stream is not required to be <i>asynchronously closeable</i>.

View File

@ -987,7 +987,7 @@ public abstract class Path
* exception then it is propogated to the iterator's {@link Iterator#hasNext()
* hasNext} or {@link Iterator#next() next} method. Where an {@code
* IOException} is thrown, it is propogated as a {@link
* java.util.concurrent.ConcurrentModificationException} with the {@code
* java.util.ConcurrentModificationException} with the {@code
* IOException} as the cause.
*
* <p> When an implementation supports operations on entries in the

View File

@ -47,6 +47,14 @@ public class SimpleFileVisitor<T> implements FileVisitor<T> {
protected SimpleFileVisitor() {
}
/**
* Throws NullPointerException if obj is null.
*/
private static void checkNotNull(Object obj) {
if (obj == null)
throw new NullPointerException();
}
/**
* Invoked for a directory before entries in the directory are visited.
*
@ -55,6 +63,7 @@ public class SimpleFileVisitor<T> implements FileVisitor<T> {
*/
@Override
public FileVisitResult preVisitDirectory(T dir) {
checkNotNull(dir);
return FileVisitResult.CONTINUE;
}
@ -70,6 +79,8 @@ public class SimpleFileVisitor<T> implements FileVisitor<T> {
*/
@Override
public FileVisitResult preVisitDirectoryFailed(T dir, IOException exc) {
checkNotNull(dir);
checkNotNull(exc);
throw new IOError(exc);
}
@ -81,6 +92,8 @@ public class SimpleFileVisitor<T> implements FileVisitor<T> {
*/
@Override
public FileVisitResult visitFile(T file, BasicFileAttributes attrs) {
checkNotNull(file);
checkNotNull(attrs);
return FileVisitResult.CONTINUE;
}
@ -96,6 +109,8 @@ public class SimpleFileVisitor<T> implements FileVisitor<T> {
*/
@Override
public FileVisitResult visitFileFailed(T file, IOException exc) {
checkNotNull(file);
checkNotNull(exc);
throw new IOError(exc);
}
@ -114,6 +129,7 @@ public class SimpleFileVisitor<T> implements FileVisitor<T> {
*/
@Override
public FileVisitResult postVisitDirectory(T dir, IOException exc) {
checkNotNull(dir);
if (exc != null)
throw new IOError(exc);
return FileVisitResult.CONTINUE;

View File

@ -75,7 +75,7 @@ import java.io.IOException;
* .lookupPrincipalByName("joe");
*
* // get view
* AclFileAttributeView view = file.newFileAttributeView(AclFileAttributeView.class);
* AclFileAttributeView view = file.getFileAttributeView(AclFileAttributeView.class);
*
* // create ACE to give "joe" read access
* AclEntry entry = AclEntry.newBuilder()

View File

@ -61,7 +61,7 @@ import java.io.IOException;
* Suppose we need to print out the owner and access permissions of a file:
* <pre>
* FileRef file = ...
* PosixFileAttributes attrs = file.newFileAttributeView(PosixFileAttributeView.class)
* PosixFileAttributes attrs = file.getFileAttributeView(PosixFileAttributeView.class)
* .readAttributes();
* System.out.format("%s %s%n",
* attrs.owner().getName(),

View File

@ -102,9 +102,9 @@
* <p><li> The {@link java.nio.file.attribute.UserPrincipalLookupService}
* interface defines methods to lookup user or group principals. </li>
*
* <p><li> The {@link java.nio.file.attribute.Attribute} interface
* <p><li> The {@link java.nio.file.attribute.FileAttribute} interface
* represents the value of an attribute for cases where the attribute value is
* require to be set atomically when creating an object in the file system. </li>
* required to be set atomically when creating an object in the file system. </li>
*
* </ul>
*

View File

@ -1065,29 +1065,103 @@ public class Arrays {
(x[b] > x[c] ? b : x[a] > x[c] ? c : a));
}
/**
* Old merge sort implementation can be selected (for
* compatibility with broken comparators) using a system property.
* Cannot be a static boolean in the enclosing class due to
* circular dependencies. To be removed in a future release.
*/
static final class LegacyMergeSort {
private static final boolean userRequested =
java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction(
"java.util.Arrays.useLegacyMergeSort")).booleanValue();
}
/*
* If this platform has an optimizing VM, check whether ComparableTimSort
* offers any performance benefit over TimSort in conjunction with a
* comparator that returns:
* {@code ((Comparable)first).compareTo(Second)}.
* If not, you are better off deleting ComparableTimSort to
* eliminate the code duplication. In other words, the commented
* out code below is the preferable implementation for sorting
* arrays of Comparables if it offers sufficient performance.
*/
// /**
// * A comparator that implements the natural ordering of a group of
// * mutually comparable elements. Using this comparator saves us
// * from duplicating most of the code in this file (one version for
// * Comparables, one for explicit Comparators).
// */
// private static final Comparator<Object> NATURAL_ORDER =
// new Comparator<Object>() {
// @SuppressWarnings("unchecked")
// public int compare(Object first, Object second) {
// return ((Comparable<Object>)first).compareTo(second);
// }
// };
//
// public static void sort(Object[] a) {
// sort(a, 0, a.length, NATURAL_ORDER);
// }
//
// public static void sort(Object[] a, int fromIndex, int toIndex) {
// sort(a, fromIndex, toIndex, NATURAL_ORDER);
// }
/**
* Sorts the specified array of objects into ascending order, according to
* the {@linkplain Comparable natural ordering}
* of its elements. All elements in the array
* must implement the {@link Comparable} interface. Furthermore, all
* elements in the array must be <i>mutually comparable</i> (that is,
* <tt>e1.compareTo(e2)</tt> must not throw a <tt>ClassCastException</tt>
* for any elements <tt>e1</tt> and <tt>e2</tt> in the array).<p>
* Sorts the specified array of objects into ascending order, according
* to the {@linkplain Comparable natural ordering} of its elements.
* All elements in the array must implement the {@link Comparable}
* interface. Furthermore, all elements in the array must be
* <i>mutually comparable</i> (that is, {@code e1.compareTo(e2)} must
* not throw a {@code ClassCastException} for any elements {@code e1}
* and {@code e2} in the array).
*
* This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.<p>
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* The sorting algorithm is a modified mergesort (in which the merge is
* omitted if the highest element in the low sublist is less than the
* lowest element in the high sublist). This algorithm offers guaranteed
* n*log(n) performance.
* <p>Implementation note: This implementation is a stable, adaptive,
* iterative mergesort that requires far fewer than n lg(n) comparisons
* when the input array is partially sorted, while offering the
* performance of a traditional mergesort when the input array is
* randomly ordered. If the input array is nearly sorted, the
* implementation requires approximately n comparisons. Temporary
* storage requirements vary from a small constant for nearly sorted
* input arrays to n/2 object references for randomly ordered input
* arrays.
*
* <p>The implementation takes equal advantage of ascending and
* descending order in its input array, and can take advantage of
* ascending and descending order in different parts of the the same
* input array. It is well-suited to merging two or more sorted arrays:
* simply concatenate the arrays and sort the resulting array.
*
* <p>The implementation was adapted from Tim Peters's list sort for Python
* (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
* TimSort</a>). It uses techiques from Peter McIlroy's "Optimistic
* Sorting and Information Theoretic Complexity", in Proceedings of the
* Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
* January 1993.
*
* @param a the array to be sorted
* @throws ClassCastException if the array contains elements that are not
* <i>mutually comparable</i> (for example, strings and integers).
* @throws ClassCastException if the array contains elements that are not
* <i>mutually comparable</i> (for example, strings and integers)
* @throws IllegalArgumentException (optional) if the natural
* ordering of the array elements is found to violate the
* {@link Comparable} contract
*/
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComparableTimSort.sort(a);
}
/** To be removed in a future release. */
private static void legacyMergeSort(Object[] a) {
Object[] aux = a.clone();
mergeSort(aux, a, 0, a.length, 0);
}
@ -1097,34 +1171,63 @@ public class Arrays {
* ascending order, according to the
* {@linkplain Comparable natural ordering} of its
* elements. The range to be sorted extends from index
* <tt>fromIndex</tt>, inclusive, to index <tt>toIndex</tt>, exclusive.
* (If <tt>fromIndex==toIndex</tt>, the range to be sorted is empty.) All
* {@code fromIndex}, inclusive, to index {@code toIndex}, exclusive.
* (If {@code fromIndex==toIndex}, the range to be sorted is empty.) All
* elements in this range must implement the {@link Comparable}
* interface. Furthermore, all elements in this range must be <i>mutually
* comparable</i> (that is, <tt>e1.compareTo(e2)</tt> must not throw a
* <tt>ClassCastException</tt> for any elements <tt>e1</tt> and
* <tt>e2</tt> in the array).<p>
* comparable</i> (that is, {@code e1.compareTo(e2)} must not throw a
* {@code ClassCastException} for any elements {@code e1} and
* {@code e2} in the array).
*
* This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.<p>
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* The sorting algorithm is a modified mergesort (in which the merge is
* omitted if the highest element in the low sublist is less than the
* lowest element in the high sublist). This algorithm offers guaranteed
* n*log(n) performance.
* <p>Implementation note: This implementation is a stable, adaptive,
* iterative mergesort that requires far fewer than n lg(n) comparisons
* when the input array is partially sorted, while offering the
* performance of a traditional mergesort when the input array is
* randomly ordered. If the input array is nearly sorted, the
* implementation requires approximately n comparisons. Temporary
* storage requirements vary from a small constant for nearly sorted
* input arrays to n/2 object references for randomly ordered input
* arrays.
*
* <p>The implementation takes equal advantage of ascending and
* descending order in its input array, and can take advantage of
* ascending and descending order in different parts of the the same
* input array. It is well-suited to merging two or more sorted arrays:
* simply concatenate the arrays and sort the resulting array.
*
* <p>The implementation was adapted from Tim Peters's list sort for Python
* (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
* TimSort</a>). It uses techiques from Peter McIlroy's "Optimistic
* Sorting and Information Theoretic Complexity", in Proceedings of the
* Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
* January 1993.
*
* @param a the array to be sorted
* @param fromIndex the index of the first element (inclusive) to be
* sorted
* @param toIndex the index of the last element (exclusive) to be sorted
* @throws IllegalArgumentException if <tt>fromIndex &gt; toIndex</tt>
* @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
* <tt>toIndex &gt; a.length</tt>
* @throws ClassCastException if the array contains elements that are
* not <i>mutually comparable</i> (for example, strings and
* integers).
* @throws IllegalArgumentException if {@code fromIndex > toIndex} or
* (optional) if the natural ordering of the array elements is
* found to violate the {@link Comparable} contract
* @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
* {@code toIndex > a.length}
* @throws ClassCastException if the array contains elements that are
* not <i>mutually comparable</i> (for example, strings and
* integers).
*/
public static void sort(Object[] a, int fromIndex, int toIndex) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex);
else
ComparableTimSort.sort(a, fromIndex, toIndex);
}
/** To be removed in a future release. */
private static void legacyMergeSort(Object[] a,
int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
Object[] aux = copyOfRange(a, fromIndex, toIndex);
mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
@ -1133,6 +1236,7 @@ public class Arrays {
/**
* Tuning parameter: list size at or below which insertion sort will be
* used in preference to mergesort or quicksort.
* To be removed in a future release.
*/
private static final int INSERTIONSORT_THRESHOLD = 7;
@ -1142,6 +1246,7 @@ public class Arrays {
* low is the index in dest to start sorting
* high is the end index in dest to end sorting
* off is the offset to generate corresponding low, high in src
* To be removed in a future release.
*/
private static void mergeSort(Object[] src,
Object[] dest,
@ -1197,25 +1302,53 @@ public class Arrays {
* Sorts the specified array of objects according to the order induced by
* the specified comparator. All elements in the array must be
* <i>mutually comparable</i> by the specified comparator (that is,
* <tt>c.compare(e1, e2)</tt> must not throw a <tt>ClassCastException</tt>
* for any elements <tt>e1</tt> and <tt>e2</tt> in the array).<p>
* {@code c.compare(e1, e2)} must not throw a {@code ClassCastException}
* for any elements {@code e1} and {@code e2} in the array).
*
* This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.<p>
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* The sorting algorithm is a modified mergesort (in which the merge is
* omitted if the highest element in the low sublist is less than the
* lowest element in the high sublist). This algorithm offers guaranteed
* n*log(n) performance.
* <p>Implementation note: This implementation is a stable, adaptive,
* iterative mergesort that requires far fewer than n lg(n) comparisons
* when the input array is partially sorted, while offering the
* performance of a traditional mergesort when the input array is
* randomly ordered. If the input array is nearly sorted, the
* implementation requires approximately n comparisons. Temporary
* storage requirements vary from a small constant for nearly sorted
* input arrays to n/2 object references for randomly ordered input
* arrays.
*
* <p>The implementation takes equal advantage of ascending and
* descending order in its input array, and can take advantage of
* ascending and descending order in different parts of the the same
* input array. It is well-suited to merging two or more sorted arrays:
* simply concatenate the arrays and sort the resulting array.
*
* <p>The implementation was adapted from Tim Peters's list sort for Python
* (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
* TimSort</a>). It uses techiques from Peter McIlroy's "Optimistic
* Sorting and Information Theoretic Complexity", in Proceedings of the
* Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
* January 1993.
*
* @param a the array to be sorted
* @param c the comparator to determine the order of the array. A
* <tt>null</tt> value indicates that the elements'
* {@code null} value indicates that the elements'
* {@linkplain Comparable natural ordering} should be used.
* @throws ClassCastException if the array contains elements that are
* not <i>mutually comparable</i> using the specified comparator.
* @throws ClassCastException if the array contains elements that are
* not <i>mutually comparable</i> using the specified comparator
* @throws IllegalArgumentException (optional) if the comparator is
* found to violate the {@link Comparator} contract
*/
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, c);
}
/** To be removed in a future release. */
private static <T> void legacyMergeSort(T[] a, Comparator<? super T> c) {
T[] aux = a.clone();
if (c==null)
mergeSort(aux, a, 0, a.length, 0);
@ -1226,36 +1359,65 @@ public class Arrays {
/**
* Sorts the specified range of the specified array of objects according
* to the order induced by the specified comparator. The range to be
* sorted extends from index <tt>fromIndex</tt>, inclusive, to index
* <tt>toIndex</tt>, exclusive. (If <tt>fromIndex==toIndex</tt>, the
* sorted extends from index {@code fromIndex}, inclusive, to index
* {@code toIndex}, exclusive. (If {@code fromIndex==toIndex}, the
* range to be sorted is empty.) All elements in the range must be
* <i>mutually comparable</i> by the specified comparator (that is,
* <tt>c.compare(e1, e2)</tt> must not throw a <tt>ClassCastException</tt>
* for any elements <tt>e1</tt> and <tt>e2</tt> in the range).<p>
* {@code c.compare(e1, e2)} must not throw a {@code ClassCastException}
* for any elements {@code e1} and {@code e2} in the range).
*
* This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.<p>
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* The sorting algorithm is a modified mergesort (in which the merge is
* omitted if the highest element in the low sublist is less than the
* lowest element in the high sublist). This algorithm offers guaranteed
* n*log(n) performance.
* <p>Implementation note: This implementation is a stable, adaptive,
* iterative mergesort that requires far fewer than n lg(n) comparisons
* when the input array is partially sorted, while offering the
* performance of a traditional mergesort when the input array is
* randomly ordered. If the input array is nearly sorted, the
* implementation requires approximately n comparisons. Temporary
* storage requirements vary from a small constant for nearly sorted
* input arrays to n/2 object references for randomly ordered input
* arrays.
*
* <p>The implementation takes equal advantage of ascending and
* descending order in its input array, and can take advantage of
* ascending and descending order in different parts of the the same
* input array. It is well-suited to merging two or more sorted arrays:
* simply concatenate the arrays and sort the resulting array.
*
* <p>The implementation was adapted from Tim Peters's list sort for Python
* (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
* TimSort</a>). It uses techiques from Peter McIlroy's "Optimistic
* Sorting and Information Theoretic Complexity", in Proceedings of the
* Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
* January 1993.
*
* @param a the array to be sorted
* @param fromIndex the index of the first element (inclusive) to be
* sorted
* @param toIndex the index of the last element (exclusive) to be sorted
* @param c the comparator to determine the order of the array. A
* <tt>null</tt> value indicates that the elements'
* {@code null} value indicates that the elements'
* {@linkplain Comparable natural ordering} should be used.
* @throws ClassCastException if the array contains elements that are not
* <i>mutually comparable</i> using the specified comparator.
* @throws IllegalArgumentException if <tt>fromIndex &gt; toIndex</tt>
* @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
* <tt>toIndex &gt; a.length</tt>
* @throws IllegalArgumentException if {@code fromIndex > toIndex} or
* (optional) if the comparator is found to violate the
* {@link Comparator} contract
* @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
* {@code toIndex > a.length}
*/
public static <T> void sort(T[] a, int fromIndex, int toIndex,
Comparator<? super T> c) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex, c);
else
TimSort.sort(a, fromIndex, toIndex, c);
}
/** To be removed in a future release. */
private static <T> void legacyMergeSort(T[] a, int fromIndex, int toIndex,
Comparator<? super T> c) {
rangeCheck(a.length, fromIndex, toIndex);
T[] aux = copyOfRange(a, fromIndex, toIndex);
if (c==null)
@ -1270,6 +1432,7 @@ public class Arrays {
* low is the index in dest to start sorting
* high is the end index in dest to end sorting
* off is the offset into src corresponding to low in dest
* To be removed in a future release.
*/
private static void mergeSort(Object[] src,
Object[] dest,

View File

@ -100,23 +100,42 @@ public class Collections {
/**
* Sorts the specified list into ascending order, according to the
* <i>natural ordering</i> of its elements. All elements in the list must
* implement the <tt>Comparable</tt> interface. Furthermore, all elements
* in the list must be <i>mutually comparable</i> (that is,
* <tt>e1.compareTo(e2)</tt> must not throw a <tt>ClassCastException</tt>
* for any elements <tt>e1</tt> and <tt>e2</tt> in the list).<p>
* {@linkplain Comparable natural ordering} of its elements.
* All elements in the list must implement the {@link Comparable}
* interface. Furthermore, all elements in the list must be
* <i>mutually comparable</i> (that is, {@code e1.compareTo(e2)}
* must not throw a {@code ClassCastException} for any elements
* {@code e1} and {@code e2} in the list).
*
* This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.<p>
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* The specified list must be modifiable, but need not be resizable.<p>
* <p>The specified list must be modifiable, but need not be resizable.
*
* The sorting algorithm is a modified mergesort (in which the merge is
* omitted if the highest element in the low sublist is less than the
* lowest element in the high sublist). This algorithm offers guaranteed
* n log(n) performance.
* <p>Implementation note: This implementation is a stable, adaptive,
* iterative mergesort that requires far fewer than n lg(n) comparisons
* when the input array is partially sorted, while offering the
* performance of a traditional mergesort when the input array is
* randomly ordered. If the input array is nearly sorted, the
* implementation requires approximately n comparisons. Temporary
* storage requirements vary from a small constant for nearly sorted
* input arrays to n/2 object references for randomly ordered input
* arrays.
*
* This implementation dumps the specified list into an array, sorts
* <p>The implementation takes equal advantage of ascending and
* descending order in its input array, and can take advantage of
* ascending and descending order in different parts of the the same
* input array. It is well-suited to merging two or more sorted arrays:
* simply concatenate the arrays and sort the resulting array.
*
* <p>The implementation was adapted from Tim Peters's list sort for Python
* (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
* TimSort</a>). It uses techiques from Peter McIlroy's "Optimistic
* Sorting and Information Theoretic Complexity", in Proceedings of the
* Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
* January 1993.
*
* <p>This implementation dumps the specified list into an array, sorts
* the array, and iterates over the list resetting each element
* from the corresponding position in the array. This avoids the
* n<sup>2</sup> log(n) performance that would result from attempting
@ -126,8 +145,10 @@ public class Collections {
* @throws ClassCastException if the list contains elements that are not
* <i>mutually comparable</i> (for example, strings and integers).
* @throws UnsupportedOperationException if the specified list's
* list-iterator does not support the <tt>set</tt> operation.
* @see Comparable
* list-iterator does not support the {@code set} operation.
* @throws IllegalArgumentException (optional) if the implementation
* detects that the natural ordering of the list elements is
* found to violate the {@link Comparable} contract
*/
public static <T extends Comparable<? super T>> void sort(List<T> list) {
Object[] a = list.toArray();
@ -143,19 +164,38 @@ public class Collections {
* Sorts the specified list according to the order induced by the
* specified comparator. All elements in the list must be <i>mutually
* comparable</i> using the specified comparator (that is,
* <tt>c.compare(e1, e2)</tt> must not throw a <tt>ClassCastException</tt>
* for any elements <tt>e1</tt> and <tt>e2</tt> in the list).<p>
* {@code c.compare(e1, e2)} must not throw a {@code ClassCastException}
* for any elements {@code e1} and {@code e2} in the list).
*
* This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.<p>
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* The sorting algorithm is a modified mergesort (in which the merge is
* omitted if the highest element in the low sublist is less than the
* lowest element in the high sublist). This algorithm offers guaranteed
* n log(n) performance.
* <p>The specified list must be modifiable, but need not be resizable.
*
* The specified list must be modifiable, but need not be resizable.
* This implementation dumps the specified list into an array, sorts
* <p>Implementation note: This implementation is a stable, adaptive,
* iterative mergesort that requires far fewer than n lg(n) comparisons
* when the input array is partially sorted, while offering the
* performance of a traditional mergesort when the input array is
* randomly ordered. If the input array is nearly sorted, the
* implementation requires approximately n comparisons. Temporary
* storage requirements vary from a small constant for nearly sorted
* input arrays to n/2 object references for randomly ordered input
* arrays.
*
* <p>The implementation takes equal advantage of ascending and
* descending order in its input array, and can take advantage of
* ascending and descending order in different parts of the the same
* input array. It is well-suited to merging two or more sorted arrays:
* simply concatenate the arrays and sort the resulting array.
*
* <p>The implementation was adapted from Tim Peters's list sort for Python
* (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
* TimSort</a>). It uses techiques from Peter McIlroy's "Optimistic
* Sorting and Information Theoretic Complexity", in Proceedings of the
* Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
* January 1993.
*
* <p>This implementation dumps the specified list into an array, sorts
* the array, and iterates over the list resetting each element
* from the corresponding position in the array. This avoids the
* n<sup>2</sup> log(n) performance that would result from attempting
@ -163,13 +203,14 @@ public class Collections {
*
* @param list the list to be sorted.
* @param c the comparator to determine the order of the list. A
* <tt>null</tt> value indicates that the elements' <i>natural
* {@code null} value indicates that the elements' <i>natural
* ordering</i> should be used.
* @throws ClassCastException if the list contains elements that are not
* <i>mutually comparable</i> using the specified comparator.
* @throws UnsupportedOperationException if the specified list's
* list-iterator does not support the <tt>set</tt> operation.
* @see Comparator
* list-iterator does not support the {@code set} operation.
* @throws IllegalArgumentException (optional) if the comparator is
* found to violate the {@link Comparator} contract
*/
public static <T> void sort(List<T> list, Comparator<? super T> c) {
Object[] a = list.toArray();

View File

@ -0,0 +1,895 @@
/*
* Copyright 2009 Google Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.util;
/**
* This is a near duplicate of {@link TimSort}, modified for use with
* arrays of objects that implement {@link Comparable}, instead of using
* explicit comparators.
*
* <p>If you are using an optimizing VM, you may find that ComparableTimSort
* offers no performance benefit over TimSort in conjunction with a
* comparator that simply returns {@code ((Comparable)first).compareTo(Second)}.
* If this is the case, you are better off deleting ComparableTimSort to
* eliminate the code duplication. (See Arrays.java for details.)
*
* @author Josh Bloch
*/
class ComparableTimSort {
/**
* This is the minimum sized sequence that will be merged. Shorter
* sequences will be lengthened by calling binarySort. If the entire
* array is less than this length, no merges will be performed.
*
* This constant should be a power of two. It was 64 in Tim Peter's C
* implementation, but 32 was empirically determined to work better in
* this implementation. In the unlikely event that you set this constant
* to be a number that's not a power of two, you'll need to change the
* {@link #minRunLength} computation.
*
* If you decrease this constant, you must change the stackLen
* computation in the TimSort constructor, or you risk an
* ArrayOutOfBounds exception. See listsort.txt for a discussion
* of the minimum stack length required as a function of the length
* of the array being sorted and the minimum merge sequence length.
*/
private static final int MIN_MERGE = 32;
/**
* The array being sorted.
*/
private final Object[] a;
/**
* When we get into galloping mode, we stay there until both runs win less
* often than MIN_GALLOP consecutive times.
*/
private static final int MIN_GALLOP = 7;
/**
* This controls when we get *into* galloping mode. It is initialized
* to MIN_GALLOP. The mergeLo and mergeHi methods nudge it higher for
* random data, and lower for highly structured data.
*/
private int minGallop = MIN_GALLOP;
/**
* Maximum initial size of tmp array, which is used for merging. The array
* can grow to accommodate demand.
*
* Unlike Tim's original C version, we do not allocate this much storage
* when sorting smaller arrays. This change was required for performance.
*/
private static final int INITIAL_TMP_STORAGE_LENGTH = 256;
/**
* Temp storage for merges.
*/
private Object[] tmp;
/**
* A stack of pending runs yet to be merged. Run i starts at
* address base[i] and extends for len[i] elements. It's always
* true (so long as the indices are in bounds) that:
*
* runBase[i] + runLen[i] == runBase[i + 1]
*
* so we could cut the storage for this, but it's a minor amount,
* and keeping all the info explicit simplifies the code.
*/
private int stackSize = 0; // Number of pending runs on stack
private final int[] runBase;
private final int[] runLen;
/**
* Creates a TimSort instance to maintain the state of an ongoing sort.
*
* @param a the array to be sorted
*/
private ComparableTimSort(Object[] a) {
this.a = a;
// Allocate temp storage (which may be increased later if necessary)
int len = a.length;
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
Object[] newArray = new Object[len < 2 * INITIAL_TMP_STORAGE_LENGTH ?
len >>> 1 : INITIAL_TMP_STORAGE_LENGTH];
tmp = newArray;
/*
* Allocate runs-to-be-merged stack (which cannot be expanded). The
* stack length requirements are described in listsort.txt. The C
* version always uses the same stack length (85), but this was
* measured to be too expensive when sorting "mid-sized" arrays (e.g.,
* 100 elements) in Java. Therefore, we use smaller (but sufficiently
* large) stack lengths for smaller arrays. The "magic numbers" in the
* computation below must be changed if MIN_MERGE is decreased. See
* the MIN_MERGE declaration above for more information.
*/
int stackLen = (len < 120 ? 5 :
len < 1542 ? 10 :
len < 119151 ? 19 : 40);
runBase = new int[stackLen];
runLen = new int[stackLen];
}
/*
* The next two methods (which are package private and static) constitute
* the entire API of this class. Each of these methods obeys the contract
* of the public method with the same signature in java.util.Arrays.
*/
static void sort(Object[] a) {
sort(a, 0, a.length);
}
static void sort(Object[] a, int lo, int hi) {
rangeCheck(a.length, lo, hi);
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi);
binarySort(a, lo, hi, lo + initRunLen);
return;
}
/**
* March over the array once, left to right, finding natural runs,
* extending short natural runs to minRun elements, and merging runs
* to maintain stack invariant.
*/
ComparableTimSort ts = new ComparableTimSort(a);
int minRun = minRunLength(nRemaining);
do {
// Identify next run
int runLen = countRunAndMakeAscending(a, lo, hi);
// If run is short, extend to min(minRun, nRemaining)
if (runLen < minRun) {
int force = nRemaining <= minRun ? nRemaining : minRun;
binarySort(a, lo, lo + force, lo + runLen);
runLen = force;
}
// Push run onto pending-run stack, and maybe merge
ts.pushRun(lo, runLen);
ts.mergeCollapse();
// Advance to find next run
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0);
// Merge all remaining runs to complete sort
assert lo == hi;
ts.mergeForceCollapse();
assert ts.stackSize == 1;
}
/**
* Sorts the specified portion of the specified array using a binary
* insertion sort. This is the best method for sorting small numbers
* of elements. It requires O(n log n) compares, but O(n^2) data
* movement (worst case).
*
* If the initial part of the specified range is already sorted,
* this method can take advantage of it: the method assumes that the
* elements from index {@code lo}, inclusive, to {@code start},
* exclusive are already sorted.
*
* @param a the array in which a range is to be sorted
* @param lo the index of the first element in the range to be sorted
* @param hi the index after the last element in the range to be sorted
* @param start the index of the first element in the range that is
* not already known to be sorted (@code lo <= start <= hi}
*/
@SuppressWarnings("fallthrough")
private static void binarySort(Object[] a, int lo, int hi, int start) {
assert lo <= start && start <= hi;
if (start == lo)
start++;
for ( ; start < hi; start++) {
@SuppressWarnings("unchecked")
Comparable<Object> pivot = (Comparable) a[start];
// Set left (and right) to the index where a[start] (pivot) belongs
int left = lo;
int right = start;
assert left <= right;
/*
* Invariants:
* pivot >= all in [lo, left).
* pivot < all in [right, start).
*/
while (left < right) {
int mid = (left + right) >>> 1;
if (pivot.compareTo(a[mid]) < 0)
right = mid;
else
left = mid + 1;
}
assert left == right;
/*
* The invariants still hold: pivot >= all in [lo, left) and
* pivot < all in [left, start), so pivot belongs at left. Note
* that if there are elements equal to pivot, left points to the
* first slot after them -- that's why this sort is stable.
* Slide elements over to make room to make room for pivot.
*/
int n = start - left; // The number of elements to move
// Switch is just an optimization for arraycopy in default case
switch(n) {
case 2: a[left + 2] = a[left + 1];
case 1: a[left + 1] = a[left];
break;
default: System.arraycopy(a, left, a, left + 1, n);
}
a[left] = pivot;
}
}
/**
* Returns the length of the run beginning at the specified position in
* the specified array and reverses the run if it is descending (ensuring
* that the run will always be ascending when the method returns).
*
* A run is the longest ascending sequence with:
*
* a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
*
* or the longest descending sequence with:
*
* a[lo] > a[lo + 1] > a[lo + 2] > ...
*
* For its intended use in a stable mergesort, the strictness of the
* definition of "descending" is needed so that the call can safely
* reverse a descending sequence without violating stability.
*
* @param a the array in which a run is to be counted and possibly reversed
* @param lo index of the first element in the run
* @param hi index after the last element that may be contained in the run.
It is required that @code{lo < hi}.
* @return the length of the run beginning at the specified position in
* the specified array
*/
@SuppressWarnings("unchecked")
private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
assert lo < hi;
int runHi = lo + 1;
if (runHi == hi)
return 1;
// Find end of run, and reverse range if descending
if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending
while(runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
runHi++;
reverseRange(a, lo, runHi);
} else { // Ascending
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)
runHi++;
}
return runHi - lo;
}
/**
* Reverse the specified range of the specified array.
*
* @param a the array in which a range is to be reversed
* @param lo the index of the first element in the range to be reversed
* @param hi the index after the last element in the range to be reversed
*/
private static void reverseRange(Object[] a, int lo, int hi) {
hi--;
while (lo < hi) {
Object t = a[lo];
a[lo++] = a[hi];
a[hi--] = t;
}
}
/**
* Returns the minimum acceptable run length for an array of the specified
* length. Natural runs shorter than this will be extended with
* {@link #binarySort}.
*
* Roughly speaking, the computation is:
*
* If n < MIN_MERGE, return n (it's too small to bother with fancy stuff).
* Else if n is an exact power of 2, return MIN_MERGE/2.
* Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k
* is close to, but strictly less than, an exact power of 2.
*
* For the rationale, see listsort.txt.
*
* @param n the length of the array to be sorted
* @return the length of the minimum run to be merged
*/
private static int minRunLength(int n) {
assert n >= 0;
int r = 0; // Becomes 1 if any 1 bits are shifted off
while (n >= MIN_MERGE) {
r |= (n & 1);
n >>= 1;
}
return n + r;
}
/**
* Pushes the specified run onto the pending-run stack.
*
* @param runBase index of the first element in the run
* @param runLen the number of elements in the run
*/
private void pushRun(int runBase, int runLen) {
this.runBase[stackSize] = runBase;
this.runLen[stackSize] = runLen;
stackSize++;
}
/**
* Examines the stack of runs waiting to be merged and merges adjacent runs
* until the stack invariants are reestablished:
*
* 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
* 2. runLen[i - 2] > runLen[i - 1]
*
* This method is called each time a new run is pushed onto the stack,
* so the invariants are guaranteed to hold for i < stackSize upon
* entry to the method.
*/
private void mergeCollapse() {
while (stackSize > 1) {
int n = stackSize - 2;
if (n > 0 && runLen[n-1] <= runLen[n] + runLen[n+1]) {
if (runLen[n - 1] < runLen[n + 1])
n--;
mergeAt(n);
} else if (runLen[n] <= runLen[n + 1]) {
mergeAt(n);
} else {
break; // Invariant is established
}
}
}
/**
* Merges all runs on the stack until only one remains. This method is
* called once, to complete the sort.
*/
private void mergeForceCollapse() {
while (stackSize > 1) {
int n = stackSize - 2;
if (n > 0 && runLen[n - 1] < runLen[n + 1])
n--;
mergeAt(n);
}
}
/**
* Merges the two runs at stack indices i and i+1. Run i must be
* the penultimate or antepenultimate run on the stack. In other words,
* i must be equal to stackSize-2 or stackSize-3.
*
* @param i stack index of the first of the two runs to merge
*/
@SuppressWarnings("unchecked")
private void mergeAt(int i) {
assert stackSize >= 2;
assert i >= 0;
assert i == stackSize - 2 || i == stackSize - 3;
int base1 = runBase[i];
int len1 = runLen[i];
int base2 = runBase[i + 1];
int len2 = runLen[i + 1];
assert len1 > 0 && len2 > 0;
assert base1 + len1 == base2;
/*
* Record the length of the combined runs; if i is the 3rd-last
* run now, also slide over the last run (which isn't involved
* in this merge). The current run (i+1) goes away in any case.
*/
runLen[i] = len1 + len2;
if (i == stackSize - 3) {
runBase[i + 1] = runBase[i + 2];
runLen[i + 1] = runLen[i + 2];
}
stackSize--;
/*
* Find where the first element of run2 goes in run1. Prior elements
* in run1 can be ignored (because they're already in place).
*/
int k = gallopRight((Comparable<Object>) a[base2], a, base1, len1, 0);
assert k >= 0;
base1 += k;
len1 -= k;
if (len1 == 0)
return;
/*
* Find where the last element of run1 goes in run2. Subsequent elements
* in run2 can be ignored (because they're already in place).
*/
len2 = gallopLeft((Comparable<Object>) a[base1 + len1 - 1], a,
base2, len2, len2 - 1);
assert len2 >= 0;
if (len2 == 0)
return;
// Merge remaining runs, using tmp array with min(len1, len2) elements
if (len1 <= len2)
mergeLo(base1, len1, base2, len2);
else
mergeHi(base1, len1, base2, len2);
}
/**
* Locates the position at which to insert the specified key into the
* specified sorted range; if the range contains an element equal to key,
* returns the index of the leftmost equal element.
*
* @param key the key whose insertion point to search for
* @param a the array in which to search
* @param base the index of the first element in the range
* @param len the length of the range; must be > 0
* @param hint the index at which to begin the search, 0 <= hint < n.
* The closer hint is to the result, the faster this method will run.
* @return the int k, 0 <= k <= n such that a[b + k - 1] < key <= a[b + k],
* pretending that a[b - 1] is minus infinity and a[b + n] is infinity.
* In other words, key belongs at index b + k; or in other words,
* the first k elements of a should precede key, and the last n - k
* should follow it.
*/
private static int gallopLeft(Comparable<Object> key, Object[] a,
int base, int len, int hint) {
assert len > 0 && hint >= 0 && hint < len;
int lastOfs = 0;
int ofs = 1;
if (key.compareTo(a[base + hint]) > 0) {
// Gallop right until a[base+hint+lastOfs] < key <= a[base+hint+ofs]
int maxOfs = len - hint;
while (ofs < maxOfs && key.compareTo(a[base + hint + ofs]) > 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs)
ofs = maxOfs;
// Make offsets relative to base
lastOfs += hint;
ofs += hint;
} else { // key <= a[base + hint]
// Gallop left until a[base+hint-ofs] < key <= a[base+hint-lastOfs]
final int maxOfs = hint + 1;
while (ofs < maxOfs && key.compareTo(a[base + hint - ofs]) <= 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs)
ofs = maxOfs;
// Make offsets relative to base
int tmp = lastOfs;
lastOfs = hint - ofs;
ofs = hint - tmp;
}
assert -1 <= lastOfs && lastOfs < ofs && ofs <= len;
/*
* Now a[base+lastOfs] < key <= a[base+ofs], so key belongs somewhere
* to the right of lastOfs but no farther right than ofs. Do a binary
* search, with invariant a[base + lastOfs - 1] < key <= a[base + ofs].
*/
lastOfs++;
while (lastOfs < ofs) {
int m = lastOfs + ((ofs - lastOfs) >>> 1);
if (key.compareTo(a[base + m]) > 0)
lastOfs = m + 1; // a[base + m] < key
else
ofs = m; // key <= a[base + m]
}
assert lastOfs == ofs; // so a[base + ofs - 1] < key <= a[base + ofs]
return ofs;
}
/**
* Like gallopLeft, except that if the range contains an element equal to
* key, gallopRight returns the index after the rightmost equal element.
*
* @param key the key whose insertion point to search for
* @param a the array in which to search
* @param base the index of the first element in the range
* @param len the length of the range; must be > 0
* @param hint the index at which to begin the search, 0 <= hint < n.
* The closer hint is to the result, the faster this method will run.
* @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k]
*/
private static int gallopRight(Comparable<Object> key, Object[] a,
int base, int len, int hint) {
assert len > 0 && hint >= 0 && hint < len;
int ofs = 1;
int lastOfs = 0;
if (key.compareTo(a[base + hint]) < 0) {
// Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs]
int maxOfs = hint + 1;
while (ofs < maxOfs && key.compareTo(a[base + hint - ofs]) < 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs)
ofs = maxOfs;
// Make offsets relative to b
int tmp = lastOfs;
lastOfs = hint - ofs;
ofs = hint - tmp;
} else { // a[b + hint] <= key
// Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs]
int maxOfs = len - hint;
while (ofs < maxOfs && key.compareTo(a[base + hint + ofs]) >= 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs)
ofs = maxOfs;
// Make offsets relative to b
lastOfs += hint;
ofs += hint;
}
assert -1 <= lastOfs && lastOfs < ofs && ofs <= len;
/*
* Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to
* the right of lastOfs but no farther right than ofs. Do a binary
* search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs].
*/
lastOfs++;
while (lastOfs < ofs) {
int m = lastOfs + ((ofs - lastOfs) >>> 1);
if (key.compareTo(a[base + m]) < 0)
ofs = m; // key < a[b + m]
else
lastOfs = m + 1; // a[b + m] <= key
}
assert lastOfs == ofs; // so a[b + ofs - 1] <= key < a[b + ofs]
return ofs;
}
/**
* Merges two adjacent runs in place, in a stable fashion. The first
* element of the first run must be greater than the first element of the
* second run (a[base1] > a[base2]), and the last element of the first run
* (a[base1 + len1-1]) must be greater than all elements of the second run.
*
* For performance, this method should be called only when len1 <= len2;
* its twin, mergeHi should be called if len1 >= len2. (Either method
* may be called if len1 == len2.)
*
* @param base1 index of first element in first run to be merged
* @param len1 length of first run to be merged (must be > 0)
* @param base2 index of first element in second run to be merged
* (must be aBase + aLen)
* @param len2 length of second run to be merged (must be > 0)
*/
@SuppressWarnings("unchecked")
private void mergeLo(int base1, int len1, int base2, int len2) {
assert len1 > 0 && len2 > 0 && base1 + len1 == base2;
// Copy first run into temp array
Object[] a = this.a; // For performance
Object[] tmp = ensureCapacity(len1);
System.arraycopy(a, base1, tmp, 0, len1);
int cursor1 = 0; // Indexes into tmp array
int cursor2 = base2; // Indexes int a
int dest = base1; // Indexes int a
// Move first element of second run and deal with degenerate cases
a[dest++] = a[cursor2++];
if (--len2 == 0) {
System.arraycopy(tmp, cursor1, a, dest, len1);
return;
}
if (len1 == 1) {
System.arraycopy(a, cursor2, a, dest, len2);
a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
return;
}
int minGallop = this.minGallop; // Use local variable for performance
outer:
while (true) {
int count1 = 0; // Number of times in a row that first run won
int count2 = 0; // Number of times in a row that second run won
/*
* Do the straightforward thing until (if ever) one run starts
* winning consistently.
*/
do {
assert len1 > 1 && len2 > 0;
if (((Comparable) a[cursor2]).compareTo(tmp[cursor1]) < 0) {
a[dest++] = a[cursor2++];
count2++;
count1 = 0;
if (--len2 == 0)
break outer;
} else {
a[dest++] = tmp[cursor1++];
count1++;
count2 = 0;
if (--len1 == 1)
break outer;
}
} while ((count1 | count2) < minGallop);
/*
* One run is winning so consistently that galloping may be a
* huge win. So try that, and continue galloping until (if ever)
* neither run appears to be winning consistently anymore.
*/
do {
assert len1 > 1 && len2 > 0;
count1 = gallopRight((Comparable) a[cursor2], tmp, cursor1, len1, 0);
if (count1 != 0) {
System.arraycopy(tmp, cursor1, a, dest, count1);
dest += count1;
cursor1 += count1;
len1 -= count1;
if (len1 <= 1) // len1 == 1 || len1 == 0
break outer;
}
a[dest++] = a[cursor2++];
if (--len2 == 0)
break outer;
count2 = gallopLeft((Comparable) tmp[cursor1], a, cursor2, len2, 0);
if (count2 != 0) {
System.arraycopy(a, cursor2, a, dest, count2);
dest += count2;
cursor2 += count2;
len2 -= count2;
if (len2 == 0)
break outer;
}
a[dest++] = tmp[cursor1++];
if (--len1 == 1)
break outer;
minGallop--;
} while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
if (minGallop < 0)
minGallop = 0;
minGallop += 2; // Penalize for leaving gallop mode
} // End of "outer" loop
this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
if (len1 == 1) {
assert len2 > 0;
System.arraycopy(a, cursor2, a, dest, len2);
a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
} else if (len1 == 0) {
throw new IllegalArgumentException(
"Comparison method violates its general contract!");
} else {
assert len2 == 0;
assert len1 > 1;
System.arraycopy(tmp, cursor1, a, dest, len1);
}
}
/**
* Like mergeLo, except that this method should be called only if
* len1 >= len2; mergeLo should be called if len1 <= len2. (Either method
* may be called if len1 == len2.)
*
* @param base1 index of first element in first run to be merged
* @param len1 length of first run to be merged (must be > 0)
* @param base2 index of first element in second run to be merged
* (must be aBase + aLen)
* @param len2 length of second run to be merged (must be > 0)
*/
@SuppressWarnings("unchecked")
private void mergeHi(int base1, int len1, int base2, int len2) {
assert len1 > 0 && len2 > 0 && base1 + len1 == base2;
// Copy second run into temp array
Object[] a = this.a; // For performance
Object[] tmp = ensureCapacity(len2);
System.arraycopy(a, base2, tmp, 0, len2);
int cursor1 = base1 + len1 - 1; // Indexes into a
int cursor2 = len2 - 1; // Indexes into tmp array
int dest = base2 + len2 - 1; // Indexes into a
// Move last element of first run and deal with degenerate cases
a[dest--] = a[cursor1--];
if (--len1 == 0) {
System.arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
return;
}
if (len2 == 1) {
dest -= len1;
cursor1 -= len1;
System.arraycopy(a, cursor1 + 1, a, dest + 1, len1);
a[dest] = tmp[cursor2];
return;
}
int minGallop = this.minGallop; // Use local variable for performance
outer:
while (true) {
int count1 = 0; // Number of times in a row that first run won
int count2 = 0; // Number of times in a row that second run won
/*
* Do the straightforward thing until (if ever) one run
* appears to win consistently.
*/
do {
assert len1 > 0 && len2 > 1;
if (((Comparable) tmp[cursor2]).compareTo(a[cursor1]) < 0) {
a[dest--] = a[cursor1--];
count1++;
count2 = 0;
if (--len1 == 0)
break outer;
} else {
a[dest--] = tmp[cursor2--];
count2++;
count1 = 0;
if (--len2 == 1)
break outer;
}
} while ((count1 | count2) < minGallop);
/*
* One run is winning so consistently that galloping may be a
* huge win. So try that, and continue galloping until (if ever)
* neither run appears to be winning consistently anymore.
*/
do {
assert len1 > 0 && len2 > 1;
count1 = len1 - gallopRight((Comparable) tmp[cursor2], a, base1, len1, len1 - 1);
if (count1 != 0) {
dest -= count1;
cursor1 -= count1;
len1 -= count1;
System.arraycopy(a, cursor1 + 1, a, dest + 1, count1);
if (len1 == 0)
break outer;
}
a[dest--] = tmp[cursor2--];
if (--len2 == 1)
break outer;
count2 = len2 - gallopLeft((Comparable) a[cursor1], tmp, 0, len2, len2 - 1);
if (count2 != 0) {
dest -= count2;
cursor2 -= count2;
len2 -= count2;
System.arraycopy(tmp, cursor2 + 1, a, dest + 1, count2);
if (len2 <= 1)
break outer; // len2 == 1 || len2 == 0
}
a[dest--] = a[cursor1--];
if (--len1 == 0)
break outer;
minGallop--;
} while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
if (minGallop < 0)
minGallop = 0;
minGallop += 2; // Penalize for leaving gallop mode
} // End of "outer" loop
this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
if (len2 == 1) {
assert len1 > 0;
dest -= len1;
cursor1 -= len1;
System.arraycopy(a, cursor1 + 1, a, dest + 1, len1);
a[dest] = tmp[cursor2]; // Move first elt of run2 to front of merge
} else if (len2 == 0) {
throw new IllegalArgumentException(
"Comparison method violates its general contract!");
} else {
assert len1 == 0;
assert len2 > 0;
System.arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
}
}
/**
* Ensures that the external array tmp has at least the specified
* number of elements, increasing its size if necessary. The size
* increases exponentially to ensure amortized linear time complexity.
*
* @param minCapacity the minimum required capacity of the tmp array
* @return tmp, whether or not it grew
*/
private Object[] ensureCapacity(int minCapacity) {
if (tmp.length < minCapacity) {
// Compute smallest power of 2 > minCapacity
int newSize = minCapacity;
newSize |= newSize >> 1;
newSize |= newSize >> 2;
newSize |= newSize >> 4;
newSize |= newSize >> 8;
newSize |= newSize >> 16;
newSize++;
if (newSize < 0) // Not bloody likely!
newSize = minCapacity;
else
newSize = Math.min(newSize, a.length >>> 1);
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
Object[] newArray = new Object[newSize];
tmp = newArray;
}
return tmp;
}
/**
* Checks that fromIndex and toIndex are in range, and throws an
* appropriate exception if they aren't.
*
* @param arrayLen the length of the array
* @param fromIndex the index of the first element of the range
* @param toIndex the index after the last element of the range
* @throws IllegalArgumentException if fromIndex > toIndex
* @throws ArrayIndexOutOfBoundsException if fromIndex < 0
* or toIndex > arrayLen
*/
private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) {
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex+")");
if (fromIndex < 0)
throw new ArrayIndexOutOfBoundsException(fromIndex);
if (toIndex > arrayLen)
throw new ArrayIndexOutOfBoundsException(toIndex);
}
}

View File

@ -2818,15 +2818,18 @@ public final class Formatter implements Closeable, Flushable {
}
private void printString(Object arg, Locale l) throws IOException {
if (arg == null) {
print("null");
} else if (arg instanceof Formattable) {
if (arg instanceof Formattable) {
Formatter fmt = formatter;
if (formatter.locale() != l)
fmt = new Formatter(formatter.out(), l);
((Formattable)arg).formatTo(fmt, f.valueOf(), width, precision);
} else {
print(arg.toString());
if (f.contains(Flags.ALTERNATE))
failMismatch(Flags.ALTERNATE, 's');
if (arg == null)
print("null");
else
print(arg.toString());
}
}

View File

@ -0,0 +1,928 @@
/*
* Copyright 2009 Google Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.util;
/**
* A stable, adaptive, iterative mergesort that requires far fewer than
* n lg(n) comparisons when running on partially sorted arrays, while
* offering performance comparable to a traditional mergesort when run
* on random arrays. Like all proper mergesorts, this sort is stable and
* runs O(n log n) time (worst case). In the worst case, this sort requires
* temporary storage space for n/2 object references; in the best case,
* it requires only a small constant amount of space.
*
* This implementation was adapted from Tim Peters's list sort for
* Python, which is described in detail here:
*
* http://svn.python.org/projects/python/trunk/Objects/listsort.txt
*
* Tim's C code may be found here:
*
* http://svn.python.org/projects/python/trunk/Objects/listobject.c
*
* The underlying techniques are described in this paper (and may have
* even earlier origins):
*
* "Optimistic Sorting and Information Theoretic Complexity"
* Peter McIlroy
* SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms),
* pp 467-474, Austin, Texas, 25-27 January 1993.
*
* While the API to this class consists solely of static methods, it is
* (privately) instantiable; a TimSort instance holds the state of an ongoing
* sort, assuming the input array is large enough to warrant the full-blown
* TimSort. Small arrays are sorted in place, using a binary insertion sort.
*
* @author Josh Bloch
*/
class TimSort<T> {
/**
* This is the minimum sized sequence that will be merged. Shorter
* sequences will be lengthened by calling binarySort. If the entire
* array is less than this length, no merges will be performed.
*
* This constant should be a power of two. It was 64 in Tim Peter's C
* implementation, but 32 was empirically determined to work better in
* this implementation. In the unlikely event that you set this constant
* to be a number that's not a power of two, you'll need to change the
* {@link #minRunLength} computation.
*
* If you decrease this constant, you must change the stackLen
* computation in the TimSort constructor, or you risk an
* ArrayOutOfBounds exception. See listsort.txt for a discussion
* of the minimum stack length required as a function of the length
* of the array being sorted and the minimum merge sequence length.
*/
private static final int MIN_MERGE = 32;
/**
* The array being sorted.
*/
private final T[] a;
/**
* The comparator for this sort.
*/
private final Comparator<? super T> c;
/**
* When we get into galloping mode, we stay there until both runs win less
* often than MIN_GALLOP consecutive times.
*/
private static final int MIN_GALLOP = 7;
/**
* This controls when we get *into* galloping mode. It is initialized
* to MIN_GALLOP. The mergeLo and mergeHi methods nudge it higher for
* random data, and lower for highly structured data.
*/
private int minGallop = MIN_GALLOP;
/**
* Maximum initial size of tmp array, which is used for merging. The array
* can grow to accommodate demand.
*
* Unlike Tim's original C version, we do not allocate this much storage
* when sorting smaller arrays. This change was required for performance.
*/
private static final int INITIAL_TMP_STORAGE_LENGTH = 256;
/**
* Temp storage for merges.
*/
private T[] tmp; // Actual runtime type will be Object[], regardless of T
/**
* A stack of pending runs yet to be merged. Run i starts at
* address base[i] and extends for len[i] elements. It's always
* true (so long as the indices are in bounds) that:
*
* runBase[i] + runLen[i] == runBase[i + 1]
*
* so we could cut the storage for this, but it's a minor amount,
* and keeping all the info explicit simplifies the code.
*/
private int stackSize = 0; // Number of pending runs on stack
private final int[] runBase;
private final int[] runLen;
/**
* Creates a TimSort instance to maintain the state of an ongoing sort.
*
* @param a the array to be sorted
* @param c the comparator to determine the order of the sort
*/
private TimSort(T[] a, Comparator<? super T> c) {
this.a = a;
this.c = c;
// Allocate temp storage (which may be increased later if necessary)
int len = a.length;
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
T[] newArray = (T[]) new Object[len < 2 * INITIAL_TMP_STORAGE_LENGTH ?
len >>> 1 : INITIAL_TMP_STORAGE_LENGTH];
tmp = newArray;
/*
* Allocate runs-to-be-merged stack (which cannot be expanded). The
* stack length requirements are described in listsort.txt. The C
* version always uses the same stack length (85), but this was
* measured to be too expensive when sorting "mid-sized" arrays (e.g.,
* 100 elements) in Java. Therefore, we use smaller (but sufficiently
* large) stack lengths for smaller arrays. The "magic numbers" in the
* computation below must be changed if MIN_MERGE is decreased. See
* the MIN_MERGE declaration above for more information.
*/
int stackLen = (len < 120 ? 5 :
len < 1542 ? 10 :
len < 119151 ? 19 : 40);
runBase = new int[stackLen];
runLen = new int[stackLen];
}
/*
* The next two methods (which are package private and static) constitute
* the entire API of this class. Each of these methods obeys the contract
* of the public method with the same signature in java.util.Arrays.
*/
static <T> void sort(T[] a, Comparator<? super T> c) {
sort(a, 0, a.length, c);
}
static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c) {
if (c == null) {
Arrays.sort(a, lo, hi);
return;
}
rangeCheck(a.length, lo, hi);
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
binarySort(a, lo, hi, lo + initRunLen, c);
return;
}
/**
* March over the array once, left to right, finding natural runs,
* extending short natural runs to minRun elements, and merging runs
* to maintain stack invariant.
*/
TimSort<T> ts = new TimSort<T>(a, c);
int minRun = minRunLength(nRemaining);
do {
// Identify next run
int runLen = countRunAndMakeAscending(a, lo, hi, c);
// If run is short, extend to min(minRun, nRemaining)
if (runLen < minRun) {
int force = nRemaining <= minRun ? nRemaining : minRun;
binarySort(a, lo, lo + force, lo + runLen, c);
runLen = force;
}
// Push run onto pending-run stack, and maybe merge
ts.pushRun(lo, runLen);
ts.mergeCollapse();
// Advance to find next run
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0);
// Merge all remaining runs to complete sort
assert lo == hi;
ts.mergeForceCollapse();
assert ts.stackSize == 1;
}
/**
* Sorts the specified portion of the specified array using a binary
* insertion sort. This is the best method for sorting small numbers
* of elements. It requires O(n log n) compares, but O(n^2) data
* movement (worst case).
*
* If the initial part of the specified range is already sorted,
* this method can take advantage of it: the method assumes that the
* elements from index {@code lo}, inclusive, to {@code start},
* exclusive are already sorted.
*
* @param a the array in which a range is to be sorted
* @param lo the index of the first element in the range to be sorted
* @param hi the index after the last element in the range to be sorted
* @param start the index of the first element in the range that is
* not already known to be sorted (@code lo <= start <= hi}
* @param c comparator to used for the sort
*/
@SuppressWarnings("fallthrough")
private static <T> void binarySort(T[] a, int lo, int hi, int start,
Comparator<? super T> c) {
assert lo <= start && start <= hi;
if (start == lo)
start++;
for ( ; start < hi; start++) {
T pivot = a[start];
// Set left (and right) to the index where a[start] (pivot) belongs
int left = lo;
int right = start;
assert left <= right;
/*
* Invariants:
* pivot >= all in [lo, left).
* pivot < all in [right, start).
*/
while (left < right) {
int mid = (left + right) >>> 1;
if (c.compare(pivot, a[mid]) < 0)
right = mid;
else
left = mid + 1;
}
assert left == right;
/*
* The invariants still hold: pivot >= all in [lo, left) and
* pivot < all in [left, start), so pivot belongs at left. Note
* that if there are elements equal to pivot, left points to the
* first slot after them -- that's why this sort is stable.
* Slide elements over to make room to make room for pivot.
*/
int n = start - left; // The number of elements to move
// Switch is just an optimization for arraycopy in default case
switch(n) {
case 2: a[left + 2] = a[left + 1];
case 1: a[left + 1] = a[left];
break;
default: System.arraycopy(a, left, a, left + 1, n);
}
a[left] = pivot;
}
}
/**
* Returns the length of the run beginning at the specified position in
* the specified array and reverses the run if it is descending (ensuring
* that the run will always be ascending when the method returns).
*
* A run is the longest ascending sequence with:
*
* a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
*
* or the longest descending sequence with:
*
* a[lo] > a[lo + 1] > a[lo + 2] > ...
*
* For its intended use in a stable mergesort, the strictness of the
* definition of "descending" is needed so that the call can safely
* reverse a descending sequence without violating stability.
*
* @param a the array in which a run is to be counted and possibly reversed
* @param lo index of the first element in the run
* @param hi index after the last element that may be contained in the run.
It is required that @code{lo < hi}.
* @param c the comparator to used for the sort
* @return the length of the run beginning at the specified position in
* the specified array
*/
private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,
Comparator<? super T> c) {
assert lo < hi;
int runHi = lo + 1;
if (runHi == hi)
return 1;
// Find end of run, and reverse range if descending
if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
while(runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
runHi++;
reverseRange(a, lo, runHi);
} else { // Ascending
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
runHi++;
}
return runHi - lo;
}
/**
* Reverse the specified range of the specified array.
*
* @param a the array in which a range is to be reversed
* @param lo the index of the first element in the range to be reversed
* @param hi the index after the last element in the range to be reversed
*/
private static void reverseRange(Object[] a, int lo, int hi) {
hi--;
while (lo < hi) {
Object t = a[lo];
a[lo++] = a[hi];
a[hi--] = t;
}
}
/**
* Returns the minimum acceptable run length for an array of the specified
* length. Natural runs shorter than this will be extended with
* {@link #binarySort}.
*
* Roughly speaking, the computation is:
*
* If n < MIN_MERGE, return n (it's too small to bother with fancy stuff).
* Else if n is an exact power of 2, return MIN_MERGE/2.
* Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k
* is close to, but strictly less than, an exact power of 2.
*
* For the rationale, see listsort.txt.
*
* @param n the length of the array to be sorted
* @return the length of the minimum run to be merged
*/
private static int minRunLength(int n) {
assert n >= 0;
int r = 0; // Becomes 1 if any 1 bits are shifted off
while (n >= MIN_MERGE) {
r |= (n & 1);
n >>= 1;
}
return n + r;
}
/**
* Pushes the specified run onto the pending-run stack.
*
* @param runBase index of the first element in the run
* @param runLen the number of elements in the run
*/
private void pushRun(int runBase, int runLen) {
this.runBase[stackSize] = runBase;
this.runLen[stackSize] = runLen;
stackSize++;
}
/**
* Examines the stack of runs waiting to be merged and merges adjacent runs
* until the stack invariants are reestablished:
*
* 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
* 2. runLen[i - 2] > runLen[i - 1]
*
* This method is called each time a new run is pushed onto the stack,
* so the invariants are guaranteed to hold for i < stackSize upon
* entry to the method.
*/
private void mergeCollapse() {
while (stackSize > 1) {
int n = stackSize - 2;
if (n > 0 && runLen[n-1] <= runLen[n] + runLen[n+1]) {
if (runLen[n - 1] < runLen[n + 1])
n--;
mergeAt(n);
} else if (runLen[n] <= runLen[n + 1]) {
mergeAt(n);
} else {
break; // Invariant is established
}
}
}
/**
* Merges all runs on the stack until only one remains. This method is
* called once, to complete the sort.
*/
private void mergeForceCollapse() {
while (stackSize > 1) {
int n = stackSize - 2;
if (n > 0 && runLen[n - 1] < runLen[n + 1])
n--;
mergeAt(n);
}
}
/**
* Merges the two runs at stack indices i and i+1. Run i must be
* the penultimate or antepenultimate run on the stack. In other words,
* i must be equal to stackSize-2 or stackSize-3.
*
* @param i stack index of the first of the two runs to merge
*/
private void mergeAt(int i) {
assert stackSize >= 2;
assert i >= 0;
assert i == stackSize - 2 || i == stackSize - 3;
int base1 = runBase[i];
int len1 = runLen[i];
int base2 = runBase[i + 1];
int len2 = runLen[i + 1];
assert len1 > 0 && len2 > 0;
assert base1 + len1 == base2;
/*
* Record the length of the combined runs; if i is the 3rd-last
* run now, also slide over the last run (which isn't involved
* in this merge). The current run (i+1) goes away in any case.
*/
runLen[i] = len1 + len2;
if (i == stackSize - 3) {
runBase[i + 1] = runBase[i + 2];
runLen[i + 1] = runLen[i + 2];
}
stackSize--;
/*
* Find where the first element of run2 goes in run1. Prior elements
* in run1 can be ignored (because they're already in place).
*/
int k = gallopRight(a[base2], a, base1, len1, 0, c);
assert k >= 0;
base1 += k;
len1 -= k;
if (len1 == 0)
return;
/*
* Find where the last element of run1 goes in run2. Subsequent elements
* in run2 can be ignored (because they're already in place).
*/
len2 = gallopLeft(a[base1 + len1 - 1], a, base2, len2, len2 - 1, c);
assert len2 >= 0;
if (len2 == 0)
return;
// Merge remaining runs, using tmp array with min(len1, len2) elements
if (len1 <= len2)
mergeLo(base1, len1, base2, len2);
else
mergeHi(base1, len1, base2, len2);
}
/**
* Locates the position at which to insert the specified key into the
* specified sorted range; if the range contains an element equal to key,
* returns the index of the leftmost equal element.
*
* @param key the key whose insertion point to search for
* @param a the array in which to search
* @param base the index of the first element in the range
* @param len the length of the range; must be > 0
* @param hint the index at which to begin the search, 0 <= hint < n.
* The closer hint is to the result, the faster this method will run.
* @param c the comparator used to order the range, and to search
* @return the int k, 0 <= k <= n such that a[b + k - 1] < key <= a[b + k],
* pretending that a[b - 1] is minus infinity and a[b + n] is infinity.
* In other words, key belongs at index b + k; or in other words,
* the first k elements of a should precede key, and the last n - k
* should follow it.
*/
private static <T> int gallopLeft(T key, T[] a, int base, int len, int hint,
Comparator<? super T> c) {
assert len > 0 && hint >= 0 && hint < len;
int lastOfs = 0;
int ofs = 1;
if (c.compare(key, a[base + hint]) > 0) {
// Gallop right until a[base+hint+lastOfs] < key <= a[base+hint+ofs]
int maxOfs = len - hint;
while (ofs < maxOfs && c.compare(key, a[base + hint + ofs]) > 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs)
ofs = maxOfs;
// Make offsets relative to base
lastOfs += hint;
ofs += hint;
} else { // key <= a[base + hint]
// Gallop left until a[base+hint-ofs] < key <= a[base+hint-lastOfs]
final int maxOfs = hint + 1;
while (ofs < maxOfs && c.compare(key, a[base + hint - ofs]) <= 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs)
ofs = maxOfs;
// Make offsets relative to base
int tmp = lastOfs;
lastOfs = hint - ofs;
ofs = hint - tmp;
}
assert -1 <= lastOfs && lastOfs < ofs && ofs <= len;
/*
* Now a[base+lastOfs] < key <= a[base+ofs], so key belongs somewhere
* to the right of lastOfs but no farther right than ofs. Do a binary
* search, with invariant a[base + lastOfs - 1] < key <= a[base + ofs].
*/
lastOfs++;
while (lastOfs < ofs) {
int m = lastOfs + ((ofs - lastOfs) >>> 1);
if (c.compare(key, a[base + m]) > 0)
lastOfs = m + 1; // a[base + m] < key
else
ofs = m; // key <= a[base + m]
}
assert lastOfs == ofs; // so a[base + ofs - 1] < key <= a[base + ofs]
return ofs;
}
/**
* Like gallopLeft, except that if the range contains an element equal to
* key, gallopRight returns the index after the rightmost equal element.
*
* @param key the key whose insertion point to search for
* @param a the array in which to search
* @param base the index of the first element in the range
* @param len the length of the range; must be > 0
* @param hint the index at which to begin the search, 0 <= hint < n.
* The closer hint is to the result, the faster this method will run.
* @param c the comparator used to order the range, and to search
* @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k]
*/
private static <T> int gallopRight(T key, T[] a, int base, int len,
int hint, Comparator<? super T> c) {
assert len > 0 && hint >= 0 && hint < len;
int ofs = 1;
int lastOfs = 0;
if (c.compare(key, a[base + hint]) < 0) {
// Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs]
int maxOfs = hint + 1;
while (ofs < maxOfs && c.compare(key, a[base + hint - ofs]) < 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs)
ofs = maxOfs;
// Make offsets relative to b
int tmp = lastOfs;
lastOfs = hint - ofs;
ofs = hint - tmp;
} else { // a[b + hint] <= key
// Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs]
int maxOfs = len - hint;
while (ofs < maxOfs && c.compare(key, a[base + hint + ofs]) >= 0) {
lastOfs = ofs;
ofs = (ofs << 1) + 1;
if (ofs <= 0) // int overflow
ofs = maxOfs;
}
if (ofs > maxOfs)
ofs = maxOfs;
// Make offsets relative to b
lastOfs += hint;
ofs += hint;
}
assert -1 <= lastOfs && lastOfs < ofs && ofs <= len;
/*
* Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to
* the right of lastOfs but no farther right than ofs. Do a binary
* search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs].
*/
lastOfs++;
while (lastOfs < ofs) {
int m = lastOfs + ((ofs - lastOfs) >>> 1);
if (c.compare(key, a[base + m]) < 0)
ofs = m; // key < a[b + m]
else
lastOfs = m + 1; // a[b + m] <= key
}
assert lastOfs == ofs; // so a[b + ofs - 1] <= key < a[b + ofs]
return ofs;
}
/**
* Merges two adjacent runs in place, in a stable fashion. The first
* element of the first run must be greater than the first element of the
* second run (a[base1] > a[base2]), and the last element of the first run
* (a[base1 + len1-1]) must be greater than all elements of the second run.
*
* For performance, this method should be called only when len1 <= len2;
* its twin, mergeHi should be called if len1 >= len2. (Either method
* may be called if len1 == len2.)
*
* @param base1 index of first element in first run to be merged
* @param len1 length of first run to be merged (must be > 0)
* @param base2 index of first element in second run to be merged
* (must be aBase + aLen)
* @param len2 length of second run to be merged (must be > 0)
*/
private void mergeLo(int base1, int len1, int base2, int len2) {
assert len1 > 0 && len2 > 0 && base1 + len1 == base2;
// Copy first run into temp array
T[] a = this.a; // For performance
T[] tmp = ensureCapacity(len1);
System.arraycopy(a, base1, tmp, 0, len1);
int cursor1 = 0; // Indexes into tmp array
int cursor2 = base2; // Indexes int a
int dest = base1; // Indexes int a
// Move first element of second run and deal with degenerate cases
a[dest++] = a[cursor2++];
if (--len2 == 0) {
System.arraycopy(tmp, cursor1, a, dest, len1);
return;
}
if (len1 == 1) {
System.arraycopy(a, cursor2, a, dest, len2);
a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
return;
}
Comparator<? super T> c = this.c; // Use local variable for performance
int minGallop = this.minGallop; // " " " " "
outer:
while (true) {
int count1 = 0; // Number of times in a row that first run won
int count2 = 0; // Number of times in a row that second run won
/*
* Do the straightforward thing until (if ever) one run starts
* winning consistently.
*/
do {
assert len1 > 1 && len2 > 0;
if (c.compare(a[cursor2], tmp[cursor1]) < 0) {
a[dest++] = a[cursor2++];
count2++;
count1 = 0;
if (--len2 == 0)
break outer;
} else {
a[dest++] = tmp[cursor1++];
count1++;
count2 = 0;
if (--len1 == 1)
break outer;
}
} while ((count1 | count2) < minGallop);
/*
* One run is winning so consistently that galloping may be a
* huge win. So try that, and continue galloping until (if ever)
* neither run appears to be winning consistently anymore.
*/
do {
assert len1 > 1 && len2 > 0;
count1 = gallopRight(a[cursor2], tmp, cursor1, len1, 0, c);
if (count1 != 0) {
System.arraycopy(tmp, cursor1, a, dest, count1);
dest += count1;
cursor1 += count1;
len1 -= count1;
if (len1 <= 1) // len1 == 1 || len1 == 0
break outer;
}
a[dest++] = a[cursor2++];
if (--len2 == 0)
break outer;
count2 = gallopLeft(tmp[cursor1], a, cursor2, len2, 0, c);
if (count2 != 0) {
System.arraycopy(a, cursor2, a, dest, count2);
dest += count2;
cursor2 += count2;
len2 -= count2;
if (len2 == 0)
break outer;
}
a[dest++] = tmp[cursor1++];
if (--len1 == 1)
break outer;
minGallop--;
} while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
if (minGallop < 0)
minGallop = 0;
minGallop += 2; // Penalize for leaving gallop mode
} // End of "outer" loop
this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
if (len1 == 1) {
assert len2 > 0;
System.arraycopy(a, cursor2, a, dest, len2);
a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
} else if (len1 == 0) {
throw new IllegalArgumentException(
"Comparison method violates its general contract!");
} else {
assert len2 == 0;
assert len1 > 1;
System.arraycopy(tmp, cursor1, a, dest, len1);
}
}
/**
* Like mergeLo, except that this method should be called only if
* len1 >= len2; mergeLo should be called if len1 <= len2. (Either method
* may be called if len1 == len2.)
*
* @param base1 index of first element in first run to be merged
* @param len1 length of first run to be merged (must be > 0)
* @param base2 index of first element in second run to be merged
* (must be aBase + aLen)
* @param len2 length of second run to be merged (must be > 0)
*/
private void mergeHi(int base1, int len1, int base2, int len2) {
assert len1 > 0 && len2 > 0 && base1 + len1 == base2;
// Copy second run into temp array
T[] a = this.a; // For performance
T[] tmp = ensureCapacity(len2);
System.arraycopy(a, base2, tmp, 0, len2);
int cursor1 = base1 + len1 - 1; // Indexes into a
int cursor2 = len2 - 1; // Indexes into tmp array
int dest = base2 + len2 - 1; // Indexes into a
// Move last element of first run and deal with degenerate cases
a[dest--] = a[cursor1--];
if (--len1 == 0) {
System.arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
return;
}
if (len2 == 1) {
dest -= len1;
cursor1 -= len1;
System.arraycopy(a, cursor1 + 1, a, dest + 1, len1);
a[dest] = tmp[cursor2];
return;
}
Comparator<? super T> c = this.c; // Use local variable for performance
int minGallop = this.minGallop; // " " " " "
outer:
while (true) {
int count1 = 0; // Number of times in a row that first run won
int count2 = 0; // Number of times in a row that second run won
/*
* Do the straightforward thing until (if ever) one run
* appears to win consistently.
*/
do {
assert len1 > 0 && len2 > 1;
if (c.compare(tmp[cursor2], a[cursor1]) < 0) {
a[dest--] = a[cursor1--];
count1++;
count2 = 0;
if (--len1 == 0)
break outer;
} else {
a[dest--] = tmp[cursor2--];
count2++;
count1 = 0;
if (--len2 == 1)
break outer;
}
} while ((count1 | count2) < minGallop);
/*
* One run is winning so consistently that galloping may be a
* huge win. So try that, and continue galloping until (if ever)
* neither run appears to be winning consistently anymore.
*/
do {
assert len1 > 0 && len2 > 1;
count1 = len1 - gallopRight(tmp[cursor2], a, base1, len1, len1 - 1, c);
if (count1 != 0) {
dest -= count1;
cursor1 -= count1;
len1 -= count1;
System.arraycopy(a, cursor1 + 1, a, dest + 1, count1);
if (len1 == 0)
break outer;
}
a[dest--] = tmp[cursor2--];
if (--len2 == 1)
break outer;
count2 = len2 - gallopLeft(a[cursor1], tmp, 0, len2, len2 - 1, c);
if (count2 != 0) {
dest -= count2;
cursor2 -= count2;
len2 -= count2;
System.arraycopy(tmp, cursor2 + 1, a, dest + 1, count2);
if (len2 <= 1) // len2 == 1 || len2 == 0
break outer;
}
a[dest--] = a[cursor1--];
if (--len1 == 0)
break outer;
minGallop--;
} while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
if (minGallop < 0)
minGallop = 0;
minGallop += 2; // Penalize for leaving gallop mode
} // End of "outer" loop
this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field
if (len2 == 1) {
assert len1 > 0;
dest -= len1;
cursor1 -= len1;
System.arraycopy(a, cursor1 + 1, a, dest + 1, len1);
a[dest] = tmp[cursor2]; // Move first elt of run2 to front of merge
} else if (len2 == 0) {
throw new IllegalArgumentException(
"Comparison method violates its general contract!");
} else {
assert len1 == 0;
assert len2 > 0;
System.arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
}
}
/**
* Ensures that the external array tmp has at least the specified
* number of elements, increasing its size if necessary. The size
* increases exponentially to ensure amortized linear time complexity.
*
* @param minCapacity the minimum required capacity of the tmp array
* @return tmp, whether or not it grew
*/
private T[] ensureCapacity(int minCapacity) {
if (tmp.length < minCapacity) {
// Compute smallest power of 2 > minCapacity
int newSize = minCapacity;
newSize |= newSize >> 1;
newSize |= newSize >> 2;
newSize |= newSize >> 4;
newSize |= newSize >> 8;
newSize |= newSize >> 16;
newSize++;
if (newSize < 0) // Not bloody likely!
newSize = minCapacity;
else
newSize = Math.min(newSize, a.length >>> 1);
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
T[] newArray = (T[]) new Object[newSize];
tmp = newArray;
}
return tmp;
}
/**
* Checks that fromIndex and toIndex are in range, and throws an
* appropriate exception if they aren't.
*
* @param arrayLen the length of the array
* @param fromIndex the index of the first element of the range
* @param toIndex the index after the last element of the range
* @throws IllegalArgumentException if fromIndex > toIndex
* @throws ArrayIndexOutOfBoundsException if fromIndex < 0
* or toIndex > arrayLen
*/
private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) {
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex+")");
if (fromIndex < 0)
throw new ArrayIndexOutOfBoundsException(fromIndex);
if (toIndex > arrayLen)
throw new ArrayIndexOutOfBoundsException(toIndex);
}
}

View File

@ -34,9 +34,13 @@
*/
package java.util.concurrent;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.AbstractQueue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;
/**
* An unbounded thread-safe {@linkplain Queue queue} based on linked nodes.
@ -47,9 +51,9 @@ import java.util.concurrent.atomic.*;
* queue the shortest time. New elements
* are inserted at the tail of the queue, and the queue retrieval
* operations obtain elements at the head of the queue.
* A <tt>ConcurrentLinkedQueue</tt> is an appropriate choice when
* A {@code ConcurrentLinkedQueue} is an appropriate choice when
* many threads will share access to a common collection.
* This queue does not permit <tt>null</tt> elements.
* This queue does not permit {@code null} elements.
*
* <p>This implementation employs an efficient &quot;wait-free&quot;
* algorithm based on one described in <a
@ -57,7 +61,7 @@ import java.util.concurrent.atomic.*;
* Fast, and Practical Non-Blocking and Blocking Concurrent Queue
* Algorithms</a> by Maged M. Michael and Michael L. Scott.
*
* <p>Beware that, unlike in most collections, the <tt>size</tt> method
* <p>Beware that, unlike in most collections, the {@code size} method
* is <em>NOT</em> a constant-time operation. Because of the
* asynchronous nature of these queues, determining the current number
* of elements requires a traversal of the elements.
@ -87,51 +91,102 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
private static final long serialVersionUID = 196745693267521676L;
/*
* This is a straight adaptation of Michael & Scott algorithm.
* For explanation, read the paper. The only (minor) algorithmic
* difference is that this version supports lazy deletion of
* internal nodes (method remove(Object)) -- remove CAS'es item
* fields to null. The normal queue operations unlink but then
* pass over nodes with null item fields. Similarly, iteration
* methods ignore those with nulls.
* This is a modification of the Michael & Scott algorithm,
* adapted for a garbage-collected environment, with support for
* interior node deletion (to support remove(Object)). For
* explanation, read the paper.
*
* Also note that like most non-blocking algorithms in this
* package, this implementation relies on the fact that in garbage
* Note that like most non-blocking algorithms in this package,
* this implementation relies on the fact that in garbage
* collected systems, there is no possibility of ABA problems due
* to recycled nodes, so there is no need to use "counted
* pointers" or related techniques seen in versions used in
* non-GC'ed settings.
*
* The fundamental invariants are:
* - There is exactly one (last) Node with a null next reference,
* which is CASed when enqueueing. This last Node can be
* reached in O(1) time from tail, but tail is merely an
* optimization - it can always be reached in O(N) time from
* head as well.
* - The elements contained in the queue are the non-null items in
* Nodes that are reachable from head. CASing the item
* reference of a Node to null atomically removes it from the
* queue. Reachability of all elements from head must remain
* true even in the case of concurrent modifications that cause
* head to advance. A dequeued Node may remain in use
* indefinitely due to creation of an Iterator or simply a
* poll() that has lost its time slice.
*
* The above might appear to imply that all Nodes are GC-reachable
* from a predecessor dequeued Node. That would cause two problems:
* - allow a rogue Iterator to cause unbounded memory retention
* - cause cross-generational linking of old Nodes to new Nodes if
* a Node was tenured while live, which generational GCs have a
* hard time dealing with, causing repeated major collections.
* However, only non-deleted Nodes need to be reachable from
* dequeued Nodes, and reachability does not necessarily have to
* be of the kind understood by the GC. We use the trick of
* linking a Node that has just been dequeued to itself. Such a
* self-link implicitly means to advance to head.
*
* Both head and tail are permitted to lag. In fact, failing to
* update them every time one could is a significant optimization
* (fewer CASes). This is controlled by local "hops" variables
* that only trigger helping-CASes after experiencing multiple
* lags.
*
* Since head and tail are updated concurrently and independently,
* it is possible for tail to lag behind head (why not)?
*
* CASing a Node's item reference to null atomically removes the
* element from the queue. Iterators skip over Nodes with null
* items. Prior implementations of this class had a race between
* poll() and remove(Object) where the same element would appear
* to be successfully removed by two concurrent operations. The
* method remove(Object) also lazily unlinks deleted Nodes, but
* this is merely an optimization.
*
* When constructing a Node (before enqueuing it) we avoid paying
* for a volatile write to item by using lazySet instead of a
* normal write. This allows the cost of enqueue to be
* "one-and-a-half" CASes.
*
* Both head and tail may or may not point to a Node with a
* non-null item. If the queue is empty, all items must of course
* be null. Upon creation, both head and tail refer to a dummy
* Node with null item. Both head and tail are only updated using
* CAS, so they never regress, although again this is merely an
* optimization.
*/
private static class Node<E> {
private volatile E item;
private volatile Node<E> next;
private static final
AtomicReferenceFieldUpdater<Node, Node>
nextUpdater =
AtomicReferenceFieldUpdater.newUpdater
(Node.class, Node.class, "next");
private static final
AtomicReferenceFieldUpdater<Node, Object>
itemUpdater =
AtomicReferenceFieldUpdater.newUpdater
(Node.class, Object.class, "item");
Node(E x) { item = x; }
Node(E x, Node<E> n) { item = x; next = n; }
Node(E item) {
// Piggyback on imminent casNext()
lazySetItem(item);
}
E getItem() {
return item;
}
boolean casItem(E cmp, E val) {
return itemUpdater.compareAndSet(this, cmp, val);
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
void setItem(E val) {
itemUpdater.set(this, val);
item = val;
}
void lazySetItem(E val) {
UNSAFE.putOrderedObject(this, itemOffset, val);
}
void lazySetNext(Node<E> val) {
UNSAFE.putOrderedObject(this, nextOffset, val);
}
Node<E> getNext() {
@ -139,52 +194,55 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
}
boolean casNext(Node<E> cmp, Node<E> val) {
return nextUpdater.compareAndSet(this, cmp, val);
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
void setNext(Node<E> val) {
nextUpdater.set(this, val);
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE =
sun.misc.Unsafe.getUnsafe();
private static final long nextOffset =
objectFieldOffset(UNSAFE, "next", Node.class);
private static final long itemOffset =
objectFieldOffset(UNSAFE, "item", Node.class);
}
private static final
AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node>
tailUpdater =
AtomicReferenceFieldUpdater.newUpdater
(ConcurrentLinkedQueue.class, Node.class, "tail");
private static final
AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node>
headUpdater =
AtomicReferenceFieldUpdater.newUpdater
(ConcurrentLinkedQueue.class, Node.class, "head");
private boolean casTail(Node<E> cmp, Node<E> val) {
return tailUpdater.compareAndSet(this, cmp, val);
}
private boolean casHead(Node<E> cmp, Node<E> val) {
return headUpdater.compareAndSet(this, cmp, val);
}
/**
* Pointer to header node, initialized to a dummy node. The first
* actual node is at head.getNext().
* A node from which the first live (non-deleted) node (if any)
* can be reached in O(1) time.
* Invariants:
* - all live nodes are reachable from head via succ()
* - head != null
* - (tmp = head).next != tmp || tmp != head
* Non-invariants:
* - head.item may or may not be null.
* - it is permitted for tail to lag behind head, that is, for tail
* to not be reachable from head!
*/
private transient volatile Node<E> head = new Node<E>(null, null);
private transient volatile Node<E> head = new Node<E>(null);
/** Pointer to last node on list **/
/**
* A node from which the last node on list (that is, the unique
* node with node.next == null) can be reached in O(1) time.
* Invariants:
* - the last node is always reachable from tail via succ()
* - tail != null
* Non-invariants:
* - tail.item may or may not be null.
* - it is permitted for tail to lag behind head, that is, for tail
* to not be reachable from head!
* - tail.next may or may not be self-pointing to tail.
*/
private transient volatile Node<E> tail = head;
/**
* Creates a <tt>ConcurrentLinkedQueue</tt> that is initially empty.
* Creates a {@code ConcurrentLinkedQueue} that is initially empty.
*/
public ConcurrentLinkedQueue() {}
/**
* Creates a <tt>ConcurrentLinkedQueue</tt>
* Creates a {@code ConcurrentLinkedQueue}
* initially containing the elements of the given collection,
* added in traversal order of the collection's iterator.
* @param c the collection of elements to initially contain
@ -201,115 +259,143 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
/**
* Inserts the specified element at the tail of this queue.
*
* @return <tt>true</tt> (as specified by {@link Collection#add})
* @return {@code true} (as specified by {@link Collection#add})
* @throws NullPointerException if the specified element is null
*/
public boolean add(E e) {
return offer(e);
}
/**
* We don't bother to update head or tail pointers if fewer than
* HOPS links from "true" location. We assume that volatile
* writes are significantly more expensive than volatile reads.
*/
private static final int HOPS = 1;
/**
* Try to CAS head to p. If successful, repoint old head to itself
* as sentinel for succ(), below.
*/
final void updateHead(Node<E> h, Node<E> p) {
if (h != p && casHead(h, p))
h.lazySetNext(h);
}
/**
* Returns the successor of p, or the head node if p.next has been
* linked to self, which will only be true if traversing with a
* stale pointer that is now off the list.
*/
final Node<E> succ(Node<E> p) {
Node<E> next = p.getNext();
return (p == next) ? head : next;
}
/**
* Inserts the specified element at the tail of this queue.
*
* @return <tt>true</tt> (as specified by {@link Queue#offer})
* @return {@code true} (as specified by {@link Queue#offer})
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
Node<E> n = new Node<E>(e, null);
Node<E> n = new Node<E>(e);
retry:
for (;;) {
Node<E> t = tail;
Node<E> s = t.getNext();
if (t == tail) {
if (s == null) {
if (t.casNext(s, n)) {
casTail(t, n);
return true;
}
Node<E> p = t;
for (int hops = 0; ; hops++) {
Node<E> next = succ(p);
if (next != null) {
if (hops > HOPS && t != tail)
continue retry;
p = next;
} else if (p.casNext(null, n)) {
if (hops >= HOPS)
casTail(t, n); // Failure is OK.
return true;
} else {
casTail(t, s);
p = succ(p);
}
}
}
}
public E poll() {
for (;;) {
Node<E> h = head;
Node<E> t = tail;
Node<E> first = h.getNext();
if (h == head) {
if (h == t) {
if (first == null)
return null;
else
casTail(t, first);
} else if (casHead(h, first)) {
E item = first.getItem();
if (item != null) {
first.setItem(null);
return item;
}
// else skip over deleted item, continue loop,
Node<E> h = head;
Node<E> p = h;
for (int hops = 0; ; hops++) {
E item = p.getItem();
if (item != null && p.casItem(item, null)) {
if (hops >= HOPS) {
Node<E> q = p.getNext();
updateHead(h, (q != null) ? q : p);
}
return item;
}
Node<E> next = succ(p);
if (next == null) {
updateHead(h, p);
break;
}
p = next;
}
return null;
}
public E peek() { // same as poll except don't remove item
public E peek() {
Node<E> h = head;
Node<E> p = h;
E item;
for (;;) {
Node<E> h = head;
Node<E> t = tail;
Node<E> first = h.getNext();
if (h == head) {
if (h == t) {
if (first == null)
return null;
else
casTail(t, first);
} else {
E item = first.getItem();
if (item != null)
return item;
else // remove deleted node and continue
casHead(h, first);
}
item = p.getItem();
if (item != null)
break;
Node<E> next = succ(p);
if (next == null) {
break;
}
p = next;
}
updateHead(h, p);
return item;
}
/**
* Returns the first actual (non-header) node on list. This is yet
* another variant of poll/peek; here returning out the first
* node, not element (so we cannot collapse with peek() without
* introducing race.)
* Returns the first live (non-deleted) node on list, or null if none.
* This is yet another variant of poll/peek; here returning the
* first node, not element. We could make peek() a wrapper around
* first(), but that would cost an extra volatile read of item,
* and the need to add a retry loop to deal with the possibility
* of losing a race to a concurrent poll().
*/
Node<E> first() {
Node<E> h = head;
Node<E> p = h;
Node<E> result;
for (;;) {
Node<E> h = head;
Node<E> t = tail;
Node<E> first = h.getNext();
if (h == head) {
if (h == t) {
if (first == null)
return null;
else
casTail(t, first);
} else {
if (first.getItem() != null)
return first;
else // remove deleted node and continue
casHead(h, first);
}
E item = p.getItem();
if (item != null) {
result = p;
break;
}
Node<E> next = succ(p);
if (next == null) {
result = null;
break;
}
p = next;
}
updateHead(h, p);
return result;
}
/**
* Returns <tt>true</tt> if this queue contains no elements.
* Returns {@code true} if this queue contains no elements.
*
* @return <tt>true</tt> if this queue contains no elements
* @return {@code true} if this queue contains no elements
*/
public boolean isEmpty() {
return first() == null;
@ -317,8 +403,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
/**
* Returns the number of elements in this queue. If this queue
* contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
* contains more than {@code Integer.MAX_VALUE} elements, returns
* {@code Integer.MAX_VALUE}.
*
* <p>Beware that, unlike in most collections, this method is
* <em>NOT</em> a constant-time operation. Because of the
@ -329,7 +415,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
*/
public int size() {
int count = 0;
for (Node<E> p = first(); p != null; p = p.getNext()) {
for (Node<E> p = first(); p != null; p = succ(p)) {
if (p.getItem() != null) {
// Collections.size() spec says to max out
if (++count == Integer.MAX_VALUE)
@ -340,16 +426,16 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
}
/**
* Returns <tt>true</tt> if this queue contains the specified element.
* More formally, returns <tt>true</tt> if and only if this queue contains
* at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.
* Returns {@code true} if this queue contains the specified element.
* More formally, returns {@code true} if and only if this queue contains
* at least one element {@code e} such that {@code o.equals(e)}.
*
* @param o object to be checked for containment in this queue
* @return <tt>true</tt> if this queue contains the specified element
* @return {@code true} if this queue contains the specified element
*/
public boolean contains(Object o) {
if (o == null) return false;
for (Node<E> p = first(); p != null; p = p.getNext()) {
for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.getItem();
if (item != null &&
o.equals(item))
@ -360,23 +446,29 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
/**
* Removes a single instance of the specified element from this queue,
* if it is present. More formally, removes an element <tt>e</tt> such
* that <tt>o.equals(e)</tt>, if this queue contains one or more such
* if it is present. More formally, removes an element {@code e} such
* that {@code o.equals(e)}, if this queue contains one or more such
* elements.
* Returns <tt>true</tt> if this queue contained the specified element
* Returns {@code true} if this queue contained the specified element
* (or equivalently, if this queue changed as a result of the call).
*
* @param o element to be removed from this queue, if present
* @return <tt>true</tt> if this queue changed as a result of the call
* @return {@code true} if this queue changed as a result of the call
*/
public boolean remove(Object o) {
if (o == null) return false;
for (Node<E> p = first(); p != null; p = p.getNext()) {
Node<E> pred = null;
for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.getItem();
if (item != null &&
o.equals(item) &&
p.casItem(item, null))
p.casItem(item, null)) {
Node<E> next = succ(p);
if (pred != null && next != null)
pred.casNext(p, next);
return true;
}
pred = p;
}
return false;
}
@ -397,7 +489,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
public Object[] toArray() {
// Use ArrayList to deal with resizing.
ArrayList<E> al = new ArrayList<E>();
for (Node<E> p = first(); p != null; p = p.getNext()) {
for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.getItem();
if (item != null)
al.add(item);
@ -415,22 +507,22 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
* <p>If this queue fits in the specified array with room to spare
* (i.e., the array has more elements than this queue), the element in
* the array immediately following the end of the queue is set to
* <tt>null</tt>.
* {@code null}.
*
* <p>Like the {@link #toArray()} method, this method acts as bridge between
* array-based and collection-based APIs. Further, this method allows
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs.
*
* <p>Suppose <tt>x</tt> is a queue known to contain only strings.
* <p>Suppose {@code x} is a queue known to contain only strings.
* The following code can be used to dump the queue into a newly
* allocated array of <tt>String</tt>:
* allocated array of {@code String}:
*
* <pre>
* String[] y = x.toArray(new String[0]);</pre>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
*
* @param a the array into which the elements of the queue are to
* be stored, if it is big enough; otherwise, a new array of the
@ -441,11 +533,12 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
* this queue
* @throws NullPointerException if the specified array is null
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
// try to use sent-in array
int k = 0;
Node<E> p;
for (p = first(); p != null && k < a.length; p = p.getNext()) {
for (p = first(); p != null && k < a.length; p = succ(p)) {
E item = p.getItem();
if (item != null)
a[k++] = (T)item;
@ -458,7 +551,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
// If won't fit, use ArrayList version
ArrayList<E> al = new ArrayList<E>();
for (Node<E> q = first(); q != null; q = q.getNext()) {
for (Node<E> q = first(); q != null; q = succ(q)) {
E item = q.getItem();
if (item != null)
al.add(item);
@ -469,7 +562,8 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
/**
* Returns an iterator over the elements in this queue in proper sequence.
* The returned iterator is a "weakly consistent" iterator that
* will never throw {@link ConcurrentModificationException},
* will never throw {@link java.util.ConcurrentModificationException
* ConcurrentModificationException},
* and guarantees to traverse elements as they existed upon
* construction of the iterator, and may (but is not guaranteed to)
* reflect any modifications subsequent to construction.
@ -511,7 +605,15 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
lastRet = nextNode;
E x = nextItem;
Node<E> p = (nextNode == null)? first() : nextNode.getNext();
Node<E> pred, p;
if (nextNode == null) {
p = first();
pred = null;
} else {
pred = nextNode;
p = succ(nextNode);
}
for (;;) {
if (p == null) {
nextNode = null;
@ -523,8 +625,13 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
nextNode = p;
nextItem = item;
return x;
} else // skip over nulls
p = p.getNext();
} else {
// skip over nulls
Node<E> next = succ(p);
if (pred != null && next != null)
pred.casNext(p, next);
p = next;
}
}
}
@ -549,7 +656,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
/**
* Save the state to a stream (that is, serialize it).
*
* @serialData All of the elements (each an <tt>E</tt>) in
* @serialData All of the elements (each an {@code E}) in
* the proper order, followed by a null
* @param s the stream
*/
@ -560,7 +667,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
s.defaultWriteObject();
// Write out all elements in the proper order.
for (Node<E> p = first(); p != null; p = p.getNext()) {
for (Node<E> p = first(); p != null; p = succ(p)) {
Object item = p.getItem();
if (item != null)
s.writeObject(item);
@ -579,10 +686,11 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
throws java.io.IOException, ClassNotFoundException {
// Read in capacity, and any hidden stuff
s.defaultReadObject();
head = new Node<E>(null, null);
head = new Node<E>(null);
tail = head;
// Read in all elements and place in queue
for (;;) {
@SuppressWarnings("unchecked")
E item = (E)s.readObject();
if (item == null)
break;
@ -591,4 +699,35 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
}
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long headOffset =
objectFieldOffset(UNSAFE, "head", ConcurrentLinkedQueue.class);
private static final long tailOffset =
objectFieldOffset(UNSAFE, "tail", ConcurrentLinkedQueue.class);
private boolean casTail(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
}
private boolean casHead(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
}
private void lazySetHead(Node<E> val) {
UNSAFE.putOrderedObject(this, headOffset, val);
}
static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
String field, Class<?> klazz) {
try {
return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
} catch (NoSuchFieldException e) {
// Convert Exception to corresponding Error
NoSuchFieldError error = new NoSuchFieldError(field);
error.initCause(e);
throw error;
}
}
}

View File

@ -34,8 +34,13 @@
*/
package java.util.concurrent;
import java.util.*;
import java.util.concurrent.locks.*;
import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* An optionally-bounded {@linkplain BlockingDeque blocking deque} based on
@ -73,6 +78,20 @@ public class LinkedBlockingDeque<E>
/*
* Implemented as a simple doubly-linked list protected by a
* single lock and using conditions to manage blocking.
*
* To implement weakly consistent iterators, it appears we need to
* keep all Nodes GC-reachable from a predecessor dequeued Node.
* That would cause two problems:
* - allow a rogue Iterator to cause unbounded memory retention
* - cause cross-generational linking of old Nodes to new Nodes if
* a Node was tenured while live, which generational GCs have a
* hard time dealing with, causing repeated major collections.
* However, only non-deleted Nodes need to be reachable from
* dequeued Nodes, and reachability does not necessarily have to
* be of the kind understood by the GC. We use the trick of
* linking a Node that has just been dequeued to itself. Such a
* self-link implicitly means to jump to "first" (for next links)
* or "last" (for prev links).
*/
/*
@ -86,9 +105,27 @@ public class LinkedBlockingDeque<E>
/** Doubly-linked list node class */
static final class Node<E> {
/**
* The item, or null if this node has been removed.
*/
E item;
/**
* One of:
* - the real predecessor Node
* - this Node, meaning the predecessor is tail
* - null, meaning there is no predecessor
*/
Node<E> prev;
/**
* One of:
* - the real successor Node
* - this Node, meaning the successor is head
* - null, meaning there is no successor
*/
Node<E> next;
Node(E x, Node<E> p, Node<E> n) {
item = x;
prev = p;
@ -96,23 +133,37 @@ public class LinkedBlockingDeque<E>
}
}
/** Pointer to first node */
private transient Node<E> first;
/** Pointer to last node */
private transient Node<E> last;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
/** Number of items in the deque */
private transient int count;
/** Maximum number of items in the deque */
private final int capacity;
/** Main lock guarding all access */
private final ReentrantLock lock = new ReentrantLock();
final ReentrantLock lock = new ReentrantLock();
/** Condition for waiting takes */
private final Condition notEmpty = lock.newCondition();
/** Condition for waiting puts */
private final Condition notFull = lock.newCondition();
/**
* Creates a <tt>LinkedBlockingDeque</tt> with a capacity of
* Creates a {@code LinkedBlockingDeque} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingDeque() {
@ -120,10 +171,10 @@ public class LinkedBlockingDeque<E>
}
/**
* Creates a <tt>LinkedBlockingDeque</tt> with the given (fixed) capacity.
* Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity.
*
* @param capacity the capacity of this deque
* @throws IllegalArgumentException if <tt>capacity</tt> is less than 1
* @throws IllegalArgumentException if {@code capacity} is less than 1
*/
public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
@ -131,7 +182,7 @@ public class LinkedBlockingDeque<E>
}
/**
* Creates a <tt>LinkedBlockingDeque</tt> with a capacity of
* Creates a {@code LinkedBlockingDeque} with a capacity of
* {@link Integer#MAX_VALUE}, initially containing the elements of
* the given collection, added in traversal order of the
* collection's iterator.
@ -142,8 +193,18 @@ public class LinkedBlockingDeque<E>
*/
public LinkedBlockingDeque(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
for (E e : c)
add(e);
final ReentrantLock lock = this.lock;
lock.lock(); // Never contended, but necessary for visibility
try {
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (!linkLast(e))
throw new IllegalStateException("Deque full");
}
} finally {
lock.unlock();
}
}
@ -153,9 +214,9 @@ public class LinkedBlockingDeque<E>
* Links e as first element, or returns false if full.
*/
private boolean linkFirst(E e) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)
return false;
++count;
Node<E> f = first;
Node<E> x = new Node<E>(e, null, f);
first = x;
@ -163,6 +224,7 @@ public class LinkedBlockingDeque<E>
last = x;
else
f.prev = x;
++count;
notEmpty.signal();
return true;
}
@ -171,9 +233,9 @@ public class LinkedBlockingDeque<E>
* Links e as last element, or returns false if full.
*/
private boolean linkLast(E e) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)
return false;
++count;
Node<E> l = last;
Node<E> x = new Node<E>(e, l, null);
last = x;
@ -181,6 +243,7 @@ public class LinkedBlockingDeque<E>
first = x;
else
l.next = x;
++count;
notEmpty.signal();
return true;
}
@ -189,10 +252,14 @@ public class LinkedBlockingDeque<E>
* Removes and returns first element, or null if empty.
*/
private E unlinkFirst() {
// assert lock.isHeldByCurrentThread();
Node<E> f = first;
if (f == null)
return null;
Node<E> n = f.next;
E item = f.item;
f.item = null;
f.next = f; // help GC
first = n;
if (n == null)
last = null;
@ -200,17 +267,21 @@ public class LinkedBlockingDeque<E>
n.prev = null;
--count;
notFull.signal();
return f.item;
return item;
}
/**
* Removes and returns last element, or null if empty.
*/
private E unlinkLast() {
// assert lock.isHeldByCurrentThread();
Node<E> l = last;
if (l == null)
return null;
Node<E> p = l.prev;
E item = l.item;
l.item = null;
l.prev = l; // help GC
last = p;
if (p == null)
first = null;
@ -218,31 +289,29 @@ public class LinkedBlockingDeque<E>
p.next = null;
--count;
notFull.signal();
return l.item;
return item;
}
/**
* Unlink e
* Unlinks x.
*/
private void unlink(Node<E> x) {
void unlink(Node<E> x) {
// assert lock.isHeldByCurrentThread();
Node<E> p = x.prev;
Node<E> n = x.next;
if (p == null) {
if (n == null)
first = last = null;
else {
n.prev = null;
first = n;
}
unlinkFirst();
} else if (n == null) {
p.next = null;
last = p;
unlinkLast();
} else {
p.next = n;
n.prev = p;
x.item = null;
// Don't mess with x's links. They may still be in use by
// an iterator.
--count;
notFull.signal();
}
--count;
notFull.signalAll();
}
// BlockingDeque methods
@ -270,6 +339,7 @@ public class LinkedBlockingDeque<E>
*/
public boolean offerFirst(E e) {
if (e == null) throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkFirst(e);
@ -283,6 +353,7 @@ public class LinkedBlockingDeque<E>
*/
public boolean offerLast(E e) {
if (e == null) throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkLast(e);
@ -297,6 +368,7 @@ public class LinkedBlockingDeque<E>
*/
public void putFirst(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
while (!linkFirst(e))
@ -312,6 +384,7 @@ public class LinkedBlockingDeque<E>
*/
public void putLast(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
while (!linkLast(e))
@ -329,15 +402,15 @@ public class LinkedBlockingDeque<E>
throws InterruptedException {
if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
if (linkFirst(e))
return true;
while (!linkFirst(e)) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
return true;
} finally {
lock.unlock();
}
@ -351,15 +424,15 @@ public class LinkedBlockingDeque<E>
throws InterruptedException {
if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
if (linkLast(e))
return true;
while (!linkLast(e)) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
return true;
} finally {
lock.unlock();
}
@ -384,6 +457,7 @@ public class LinkedBlockingDeque<E>
}
public E pollFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkFirst();
@ -393,6 +467,7 @@ public class LinkedBlockingDeque<E>
}
public E pollLast() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkLast();
@ -402,6 +477,7 @@ public class LinkedBlockingDeque<E>
}
public E takeFirst() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
@ -414,6 +490,7 @@ public class LinkedBlockingDeque<E>
}
public E takeLast() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
@ -428,16 +505,16 @@ public class LinkedBlockingDeque<E>
public E pollFirst(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E x = unlinkFirst();
if (x != null)
return x;
E x;
while ( (x = unlinkFirst()) == null) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return x;
} finally {
lock.unlock();
}
@ -446,16 +523,16 @@ public class LinkedBlockingDeque<E>
public E pollLast(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E x = unlinkLast();
if (x != null)
return x;
E x;
while ( (x = unlinkLast()) == null) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return x;
} finally {
lock.unlock();
}
@ -480,6 +557,7 @@ public class LinkedBlockingDeque<E>
}
public E peekFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (first == null) ? null : first.item;
@ -489,6 +567,7 @@ public class LinkedBlockingDeque<E>
}
public E peekLast() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (last == null) ? null : last.item;
@ -499,6 +578,7 @@ public class LinkedBlockingDeque<E>
public boolean removeFirstOccurrence(Object o) {
if (o == null) return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
for (Node<E> p = first; p != null; p = p.next) {
@ -515,6 +595,7 @@ public class LinkedBlockingDeque<E>
public boolean removeLastOccurrence(Object o) {
if (o == null) return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
for (Node<E> p = last; p != null; p = p.prev) {
@ -619,14 +700,15 @@ public class LinkedBlockingDeque<E>
* Returns the number of additional elements that this deque can ideally
* (in the absence of memory or resource constraints) accept without
* blocking. This is always equal to the initial capacity of this deque
* less the current <tt>size</tt> of this deque.
* less the current {@code size} of this deque.
*
* <p>Note that you <em>cannot</em> always tell if an attempt to insert
* an element will succeed by inspecting <tt>remainingCapacity</tt>
* an element will succeed by inspecting {@code remainingCapacity}
* because it may be the case that another thread is about to
* insert or remove an element.
*/
public int remainingCapacity() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return capacity - count;
@ -642,22 +724,7 @@ public class LinkedBlockingDeque<E>
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
lock.lock();
try {
for (Node<E> p = first; p != null; p = p.next)
c.add(p.item);
int n = count;
count = 0;
first = last = null;
notFull.signalAll();
return n;
} finally {
lock.unlock();
}
return drainTo(c, Integer.MAX_VALUE);
}
/**
@ -671,19 +738,14 @@ public class LinkedBlockingDeque<E>
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
int n = 0;
while (n < maxElements && first != null) {
c.add(first.item);
first.prev = null;
first = first.next;
--count;
++n;
int n = Math.min(maxElements, count);
for (int i = 0; i < n; i++) {
c.add(first.item); // In this order, in case add() throws.
unlinkFirst();
}
if (first == null)
last = null;
notFull.signalAll();
return n;
} finally {
lock.unlock();
@ -712,16 +774,16 @@ public class LinkedBlockingDeque<E>
/**
* Removes the first occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
* More formally, removes the first element <tt>e</tt> such that
* <tt>o.equals(e)</tt> (if such an element exists).
* Returns <tt>true</tt> if this deque contained the specified element
* More formally, removes the first element {@code e} such that
* {@code o.equals(e)} (if such an element exists).
* Returns {@code true} if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
*
* <p>This method is equivalent to
* {@link #removeFirstOccurrence(Object) removeFirstOccurrence}.
*
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> if this deque changed as a result of the call
* @return {@code true} if this deque changed as a result of the call
*/
public boolean remove(Object o) {
return removeFirstOccurrence(o);
@ -733,6 +795,7 @@ public class LinkedBlockingDeque<E>
* @return the number of elements in this deque
*/
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return count;
@ -742,15 +805,16 @@ public class LinkedBlockingDeque<E>
}
/**
* Returns <tt>true</tt> if this deque contains the specified element.
* More formally, returns <tt>true</tt> if and only if this deque contains
* at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.
* Returns {@code true} if this deque contains the specified element.
* More formally, returns {@code true} if and only if this deque contains
* at least one element {@code e} such that {@code o.equals(e)}.
*
* @param o object to be checked for containment in this deque
* @return <tt>true</tt> if this deque contains the specified element
* @return {@code true} if this deque contains the specified element
*/
public boolean contains(Object o) {
if (o == null) return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
for (Node<E> p = first; p != null; p = p.next)
@ -762,24 +826,46 @@ public class LinkedBlockingDeque<E>
}
}
/**
* Variant of removeFirstOccurrence needed by iterator.remove.
* Searches for the node, not its contents.
/*
* TODO: Add support for more efficient bulk operations.
*
* We don't want to acquire the lock for every iteration, but we
* also want other threads a chance to interact with the
* collection, especially when count is close to capacity.
*/
boolean removeNode(Node<E> e) {
lock.lock();
try {
for (Node<E> p = first; p != null; p = p.next) {
if (p == e) {
unlink(p);
return true;
}
}
return false;
} finally {
lock.unlock();
}
}
// /**
// * Adds all of the elements in the specified collection to this
// * queue. Attempts to addAll of a queue to itself result in
// * {@code IllegalArgumentException}. Further, the behavior of
// * this operation is undefined if the specified collection is
// * modified while the operation is in progress.
// *
// * @param c collection containing elements to be added to this queue
// * @return {@code true} if this queue changed as a result of the call
// * @throws ClassCastException {@inheritDoc}
// * @throws NullPointerException {@inheritDoc}
// * @throws IllegalArgumentException {@inheritDoc}
// * @throws IllegalStateException {@inheritDoc}
// * @see #add(Object)
// */
// public boolean addAll(Collection<? extends E> c) {
// if (c == null)
// throw new NullPointerException();
// if (c == this)
// throw new IllegalArgumentException();
// final ReentrantLock lock = this.lock;
// lock.lock();
// try {
// boolean modified = false;
// for (E e : c)
// if (linkLast(e))
// modified = true;
// return modified;
// } finally {
// lock.unlock();
// }
// }
/**
* Returns an array containing all of the elements in this deque, in
@ -794,7 +880,9 @@ public class LinkedBlockingDeque<E>
*
* @return an array containing all of the elements in this deque
*/
@SuppressWarnings("unchecked")
public Object[] toArray() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] a = new Object[count];
@ -817,22 +905,22 @@ public class LinkedBlockingDeque<E>
* <p>If this deque fits in the specified array with room to spare
* (i.e., the array has more elements than this deque), the element in
* the array immediately following the end of the deque is set to
* <tt>null</tt>.
* {@code null}.
*
* <p>Like the {@link #toArray()} method, this method acts as bridge between
* array-based and collection-based APIs. Further, this method allows
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs.
*
* <p>Suppose <tt>x</tt> is a deque known to contain only strings.
* <p>Suppose {@code x} is a deque known to contain only strings.
* The following code can be used to dump the deque into a newly
* allocated array of <tt>String</tt>:
* allocated array of {@code String}:
*
* <pre>
* String[] y = x.toArray(new String[0]);</pre>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
*
* @param a the array into which the elements of the deque are to
* be stored, if it is big enough; otherwise, a new array of the
@ -843,14 +931,14 @@ public class LinkedBlockingDeque<E>
* this deque
* @throws NullPointerException if the specified array is null
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (a.length < count)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(),
count
);
a = (T[])java.lang.reflect.Array.newInstance
(a.getClass().getComponentType(), count);
int k = 0;
for (Node<E> p = first; p != null; p = p.next)
@ -864,6 +952,7 @@ public class LinkedBlockingDeque<E>
}
public String toString() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return super.toString();
@ -877,8 +966,16 @@ public class LinkedBlockingDeque<E>
* The deque will be empty after this call returns.
*/
public void clear() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
for (Node<E> f = first; f != null; ) {
f.item = null;
Node<E> n = f.next;
f.prev = null;
f.next = null;
f = n;
}
first = last = null;
count = 0;
notFull.signalAll();
@ -890,8 +987,9 @@ public class LinkedBlockingDeque<E>
/**
* Returns an iterator over the elements in this deque in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
* The returned <tt>Iterator</tt> is a "weakly consistent" iterator that
* will never throw {@link ConcurrentModificationException},
* The returned {@code Iterator} is a "weakly consistent" iterator that
* will never throw {@link java.util.ConcurrentModificationException
* ConcurrentModificationException},
* and guarantees to traverse elements as they existed upon
* construction of the iterator, and may (but is not guaranteed to)
* reflect any modifications subsequent to construction.
@ -906,8 +1004,9 @@ public class LinkedBlockingDeque<E>
* Returns an iterator over the elements in this deque in reverse
* sequential order. The elements will be returned in order from
* last (tail) to first (head).
* The returned <tt>Iterator</tt> is a "weakly consistent" iterator that
* will never throw {@link ConcurrentModificationException},
* The returned {@code Iterator} is a "weakly consistent" iterator that
* will never throw {@link java.util.ConcurrentModificationException
* ConcurrentModificationException},
* and guarantees to traverse elements as they existed upon
* construction of the iterator, and may (but is not guaranteed to)
* reflect any modifications subsequent to construction.
@ -921,7 +1020,7 @@ public class LinkedBlockingDeque<E>
*/
private abstract class AbstractItr implements Iterator<E> {
/**
* The next node to return in next
* The next node to return in next()
*/
Node<E> next;
@ -939,15 +1038,44 @@ public class LinkedBlockingDeque<E>
*/
private Node<E> lastRet;
abstract Node<E> firstNode();
abstract Node<E> nextNode(Node<E> n);
AbstractItr() {
advance(); // set to initial position
// set to initial position
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
next = firstNode();
nextItem = (next == null) ? null : next.item;
} finally {
lock.unlock();
}
}
/**
* Advances next, or if not yet initialized, sets to first node.
* Implemented to move forward vs backward in the two subclasses.
* Advances next.
*/
abstract void advance();
void advance() {
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
// assert next != null;
Node<E> s = nextNode(next);
if (s == next) {
next = firstNode();
} else {
// Skip over removed nodes.
// May be necessary if multiple interior Nodes are removed.
while (s != null && s.item == null)
s = nextNode(s);
next = s;
}
nextItem = (next == null) ? null : next.item;
} finally {
lock.unlock();
}
}
public boolean hasNext() {
return next != null;
@ -967,52 +1095,39 @@ public class LinkedBlockingDeque<E>
if (n == null)
throw new IllegalStateException();
lastRet = null;
// Note: removeNode rescans looking for this node to make
// sure it was not already removed. Otherwise, trying to
// re-remove could corrupt list.
removeNode(n);
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
if (n.item != null)
unlink(n);
} finally {
lock.unlock();
}
}
}
/** Forward iterator */
private class Itr extends AbstractItr {
void advance() {
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
next = (next == null)? first : next.next;
nextItem = (next == null)? null : next.item;
} finally {
lock.unlock();
}
}
Node<E> firstNode() { return first; }
Node<E> nextNode(Node<E> n) { return n.next; }
}
/**
* Descending iterator for LinkedBlockingDeque
*/
/** Descending iterator */
private class DescendingItr extends AbstractItr {
void advance() {
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
next = (next == null)? last : next.prev;
nextItem = (next == null)? null : next.item;
} finally {
lock.unlock();
}
}
Node<E> firstNode() { return last; }
Node<E> nextNode(Node<E> n) { return n.prev; }
}
/**
* Save the state of this deque to a stream (that is, serialize it).
*
* @serialData The capacity (int), followed by elements (each an
* <tt>Object</tt>) in the proper order, followed by a null
* {@code Object}) in the proper order, followed by a null
* @param s the stream
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// Write out capacity and any hidden stuff
@ -1040,6 +1155,7 @@ public class LinkedBlockingDeque<E>
last = null;
// Read in all elements and place in queue
for (;;) {
@SuppressWarnings("unchecked")
E item = (E)s.readObject();
if (item == null)
break;

View File

@ -34,9 +34,14 @@
*/
package java.util.concurrent;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* An optionally-bounded {@linkplain BlockingQueue blocking queue} based on
@ -86,15 +91,43 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* items have been entered since the signal. And symmetrically for
* takes signalling puts. Operations such as remove(Object) and
* iterators acquire both locks.
*
* Visibility between writers and readers is provided as follows:
*
* Whenever an element is enqueued, the putLock is acquired and
* count updated. A subsequent reader guarantees visibility to the
* enqueued Node by either acquiring the putLock (via fullyLock)
* or by acquiring the takeLock, and then reading n = count.get();
* this gives visibility to the first n items.
*
* To implement weakly consistent iterators, it appears we need to
* keep all Nodes GC-reachable from a predecessor dequeued Node.
* That would cause two problems:
* - allow a rogue Iterator to cause unbounded memory retention
* - cause cross-generational linking of old Nodes to new Nodes if
* a Node was tenured while live, which generational GCs have a
* hard time dealing with, causing repeated major collections.
* However, only non-deleted Nodes need to be reachable from
* dequeued Nodes, and reachability does not necessarily have to
* be of the kind understood by the GC. We use the trick of
* linking a Node that has just been dequeued to itself. Such a
* self-link implicitly means to advance to head.next.
*/
/**
* Linked list node class
*/
static class Node<E> {
/** The item, volatile to ensure barrier separating write and read */
volatile E item;
E item;
/**
* One of:
* - the real successor Node
* - this Node, meaning the successor is head.next
* - null, meaning there is no successor (this is the last node)
*/
Node<E> next;
Node(E x) { item = x; }
}
@ -104,10 +137,16 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
/** Current number of elements */
private final AtomicInteger count = new AtomicInteger(0);
/** Head of linked list */
/**
* Head of linked list.
* Invariant: head.item == null
*/
private transient Node<E> head;
/** Tail of linked list */
/**
* Tail of linked list.
* Invariant: last.next == null
*/
private transient Node<E> last;
/** Lock held by take, poll, etc */
@ -151,18 +190,26 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
/**
* Creates a node and links it at end of queue.
*
* @param x the item
*/
private void insert(E x) {
private void enqueue(E x) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = new Node<E>(x);
}
/**
* Removes a node from head of queue,
* Removes a node from head of queue.
*
* @return the node
*/
private E extract() {
Node<E> first = head.next;
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
@ -172,7 +219,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
/**
* Lock to prevent both puts and takes.
*/
private void fullyLock() {
void fullyLock() {
putLock.lock();
takeLock.lock();
}
@ -180,14 +227,21 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
/**
* Unlock to allow both puts and takes.
*/
private void fullyUnlock() {
void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}
// /**
// * Tells whether both locks are held by current thread.
// */
// boolean isFullyLocked() {
// return (putLock.isHeldByCurrentThread() &&
// takeLock.isHeldByCurrentThread());
// }
/**
* Creates a <tt>LinkedBlockingQueue</tt> with a capacity of
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
@ -195,10 +249,10 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
/**
* Creates a <tt>LinkedBlockingQueue</tt> with the given (fixed) capacity.
* Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
*
* @param capacity the capacity of this queue
* @throws IllegalArgumentException if <tt>capacity</tt> is not greater
* @throws IllegalArgumentException if {@code capacity} is not greater
* than zero
*/
public LinkedBlockingQueue(int capacity) {
@ -208,7 +262,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
/**
* Creates a <tt>LinkedBlockingQueue</tt> with a capacity of
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}, initially containing the elements of the
* given collection,
* added in traversal order of the collection's iterator.
@ -219,8 +273,22 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
*/
public LinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
for (E e : c)
add(e);
final ReentrantLock putLock = this.putLock;
putLock.lock(); // Never contended, but necessary for visibility
try {
int n = 0;
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (n == capacity)
throw new IllegalStateException("Queue full");
enqueue(e);
++n;
}
count.set(n);
} finally {
putLock.unlock();
}
}
@ -241,10 +309,10 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* Returns the number of additional elements that this queue can ideally
* (in the absence of memory or resource constraints) accept without
* blocking. This is always equal to the initial capacity of this queue
* less the current <tt>size</tt> of this queue.
* less the current {@code size} of this queue.
*
* <p>Note that you <em>cannot</em> always tell if an attempt to insert
* an element will succeed by inspecting <tt>remainingCapacity</tt>
* an element will succeed by inspecting {@code remainingCapacity}
* because it may be the case that another thread is about to
* insert or remove an element.
*/
@ -261,8 +329,8 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
*/
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset
// local var holding count negative to indicate failure unless set.
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
@ -273,18 +341,13 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* not protected by lock. This works because count can
* only decrease at this point (all other puts are shut
* out by lock), and we (or some other waiting put) are
* signalled if it ever changes from
* capacity. Similarly for all other uses of count in
* other wait guards.
* signalled if it ever changes from capacity. Similarly
* for all other uses of count in other wait guards.
*/
try {
while (count.get() == capacity)
notFull.await();
} catch (InterruptedException ie) {
notFull.signal(); // propagate to a non-interrupted thread
throw ie;
while (count.get() == capacity) {
notFull.await();
}
insert(e);
enqueue(e);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
@ -299,7 +362,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* Inserts the specified element at the tail of this queue, waiting if
* necessary up to the specified wait time for space to become available.
*
* @return <tt>true</tt> if successful, or <tt>false</tt> if
* @return {@code true} if successful, or {@code false} if
* the specified waiting time elapses before space is available.
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
@ -314,23 +377,15 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
for (;;) {
if (count.get() < capacity) {
insert(e);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
break;
}
while (count.get() == capacity) {
if (nanos <= 0)
return false;
try {
nanos = notFull.awaitNanos(nanos);
} catch (InterruptedException ie) {
notFull.signal(); // propagate to a non-interrupted thread
throw ie;
}
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
@ -342,7 +397,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
/**
* Inserts the specified element at the tail of this queue if it is
* possible to do so immediately without exceeding the queue's capacity,
* returning <tt>true</tt> upon success and <tt>false</tt> if this queue
* returning {@code true} upon success and {@code false} if this queue
* is full.
* When using a capacity-restricted queue, this method is generally
* preferable to method {@link BlockingQueue#add add}, which can fail to
@ -360,7 +415,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
putLock.lock();
try {
if (count.get() < capacity) {
insert(e);
enqueue(e);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
@ -381,15 +436,10 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
try {
while (count.get() == 0)
notEmpty.await();
} catch (InterruptedException ie) {
notEmpty.signal(); // propagate to a non-interrupted thread
throw ie;
while (count.get() == 0) {
notEmpty.await();
}
x = extract();
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
@ -409,23 +459,15 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
for (;;) {
if (count.get() > 0) {
x = extract();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
break;
}
while (count.get() == 0) {
if (nanos <= 0)
return null;
try {
nanos = notEmpty.awaitNanos(nanos);
} catch (InterruptedException ie) {
notEmpty.signal(); // propagate to a non-interrupted thread
throw ie;
}
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
@ -444,7 +486,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
takeLock.lock();
try {
if (count.get() > 0) {
x = extract();
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
@ -457,7 +499,6 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
return x;
}
public E peek() {
if (count.get() == 0)
return null;
@ -474,44 +515,48 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
}
}
/**
* Unlinks interior Node p with predecessor trail.
*/
void unlink(Node<E> p, Node<E> trail) {
// assert isFullyLocked();
// p.next is not changed, to allow iterators that are
// traversing p to maintain their weak-consistency guarantee.
p.item = null;
trail.next = p.next;
if (last == p)
last = trail;
if (count.getAndDecrement() == capacity)
notFull.signal();
}
/**
* Removes a single instance of the specified element from this queue,
* if it is present. More formally, removes an element <tt>e</tt> such
* that <tt>o.equals(e)</tt>, if this queue contains one or more such
* if it is present. More formally, removes an element {@code e} such
* that {@code o.equals(e)}, if this queue contains one or more such
* elements.
* Returns <tt>true</tt> if this queue contained the specified element
* Returns {@code true} if this queue contained the specified element
* (or equivalently, if this queue changed as a result of the call).
*
* @param o element to be removed from this queue, if present
* @return <tt>true</tt> if this queue changed as a result of the call
* @return {@code true} if this queue changed as a result of the call
*/
public boolean remove(Object o) {
if (o == null) return false;
boolean removed = false;
fullyLock();
try {
Node<E> trail = head;
Node<E> p = head.next;
while (p != null) {
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (o.equals(p.item)) {
removed = true;
break;
unlink(p, trail);
return true;
}
trail = p;
p = p.next;
}
if (removed) {
p.item = null;
trail.next = p.next;
if (last == p)
last = trail;
if (count.getAndDecrement() == capacity)
notFull.signalAll();
}
return false;
} finally {
fullyUnlock();
}
return removed;
}
/**
@ -551,22 +596,22 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* <p>If this queue fits in the specified array with room to spare
* (i.e., the array has more elements than this queue), the element in
* the array immediately following the end of the queue is set to
* <tt>null</tt>.
* {@code null}.
*
* <p>Like the {@link #toArray()} method, this method acts as bridge between
* array-based and collection-based APIs. Further, this method allows
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs.
*
* <p>Suppose <tt>x</tt> is a queue known to contain only strings.
* <p>Suppose {@code x} is a queue known to contain only strings.
* The following code can be used to dump the queue into a newly
* allocated array of <tt>String</tt>:
* allocated array of {@code String}:
*
* <pre>
* String[] y = x.toArray(new String[0]);</pre>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
*
* @param a the array into which the elements of the queue are to
* be stored, if it is big enough; otherwise, a new array of the
@ -577,6 +622,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* this queue
* @throws NullPointerException if the specified array is null
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
fullyLock();
try {
@ -586,7 +632,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
(a.getClass().getComponentType(), size);
int k = 0;
for (Node p = head.next; p != null; p = p.next)
for (Node<E> p = head.next; p != null; p = p.next)
a[k++] = (T)p.item;
if (a.length > k)
a[k] = null;
@ -612,11 +658,14 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
public void clear() {
fullyLock();
try {
head.next = null;
assert head.item == null;
last = head;
for (Node<E> p, h = head; (p = h.next) != null; h = p) {
h.next = h;
p.item = null;
}
head = last;
// assert head.item == null && head.next == null;
if (count.getAndSet(0) == capacity)
notFull.signalAll();
notFull.signal();
} finally {
fullyUnlock();
}
@ -629,30 +678,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
Node<E> first;
fullyLock();
try {
first = head.next;
head.next = null;
assert head.item == null;
last = head;
if (count.getAndSet(0) == capacity)
notFull.signalAll();
} finally {
fullyUnlock();
}
// Transfer the elements outside of locks
int n = 0;
for (Node<E> p = first; p != null; p = p.next) {
c.add(p.item);
p.item = null;
++n;
}
return n;
return drainTo(c, Integer.MAX_VALUE);
}
/**
@ -666,34 +692,44 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
fullyLock();
boolean signalNotFull = false;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
int n = 0;
Node<E> p = head.next;
while (p != null && n < maxElements) {
c.add(p.item);
p.item = null;
p = p.next;
++n;
int n = Math.min(maxElements, count.get());
// count.get provides visibility to first n Nodes
Node<E> h = head;
int i = 0;
try {
while (i < n) {
Node<E> p = h.next;
c.add(p.item);
p.item = null;
h.next = h;
h = p;
++i;
}
return n;
} finally {
// Restore invariants even if c.add() threw
if (i > 0) {
// assert h.item == null;
head = h;
signalNotFull = (count.getAndAdd(-i) == capacity);
}
}
if (n != 0) {
head.next = p;
assert head.item == null;
if (p == null)
last = head;
if (count.getAndAdd(-n) == capacity)
notFull.signalAll();
}
return n;
} finally {
fullyUnlock();
takeLock.unlock();
if (signalNotFull)
signalNotFull();
}
}
/**
* Returns an iterator over the elements in this queue in proper sequence.
* The returned <tt>Iterator</tt> is a "weakly consistent" iterator that
* will never throw {@link ConcurrentModificationException},
* The returned {@code Iterator} is a "weakly consistent" iterator that
* will never throw {@link java.util.ConcurrentModificationException
* ConcurrentModificationException},
* and guarantees to traverse elements as they existed upon
* construction of the iterator, and may (but is not guaranteed to)
* reflect any modifications subsequent to construction.
@ -706,7 +742,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
private class Itr implements Iterator<E> {
/*
* Basic weak-consistent iterator. At all times hold the next
* Basic weakly-consistent iterator. At all times hold the next
* item to hand out so that if hasNext() reports true, we will
* still have it to return even if lost race with a take etc.
*/
@ -715,17 +751,13 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
private E currentElement;
Itr() {
final ReentrantLock putLock = LinkedBlockingQueue.this.putLock;
final ReentrantLock takeLock = LinkedBlockingQueue.this.takeLock;
putLock.lock();
takeLock.lock();
fullyLock();
try {
current = head.next;
if (current != null)
currentElement = current.item;
} finally {
takeLock.unlock();
putLock.unlock();
fullyUnlock();
}
}
@ -733,54 +765,54 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
return current != null;
}
/**
* Unlike other traversal methods, iterators need to handle:
* - dequeued nodes (p.next == p)
* - interior removed nodes (p.item == null)
*/
private Node<E> nextNode(Node<E> p) {
Node<E> s = p.next;
if (p == s)
return head.next;
// Skip over removed nodes.
// May be necessary if multiple interior Nodes are removed.
while (s != null && s.item == null)
s = s.next;
return s;
}
public E next() {
final ReentrantLock putLock = LinkedBlockingQueue.this.putLock;
final ReentrantLock takeLock = LinkedBlockingQueue.this.takeLock;
putLock.lock();
takeLock.lock();
fullyLock();
try {
if (current == null)
throw new NoSuchElementException();
E x = currentElement;
lastRet = current;
current = current.next;
if (current != null)
currentElement = current.item;
current = nextNode(current);
currentElement = (current == null) ? null : current.item;
return x;
} finally {
takeLock.unlock();
putLock.unlock();
fullyUnlock();
}
}
public void remove() {
if (lastRet == null)
throw new IllegalStateException();
final ReentrantLock putLock = LinkedBlockingQueue.this.putLock;
final ReentrantLock takeLock = LinkedBlockingQueue.this.takeLock;
putLock.lock();
takeLock.lock();
fullyLock();
try {
Node<E> node = lastRet;
lastRet = null;
Node<E> trail = head;
Node<E> p = head.next;
while (p != null && p != node) {
trail = p;
p = p.next;
}
if (p == node) {
p.item = null;
trail.next = p.next;
if (last == p)
last = trail;
int c = count.getAndDecrement();
if (c == capacity)
notFull.signalAll();
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (p == node) {
unlink(p, trail);
break;
}
}
} finally {
takeLock.unlock();
putLock.unlock();
fullyUnlock();
}
}
}
@ -789,7 +821,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
* Save the state to a stream (that is, serialize it).
*
* @serialData The capacity is emitted (int), followed by all of
* its elements (each an <tt>Object</tt>) in the proper order,
* its elements (each an {@code Object}) in the proper order,
* followed by a null
* @param s the stream
*/
@ -815,6 +847,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
/**
* Reconstitute this queue instance from a stream (that is,
* deserialize it).
*
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
@ -827,6 +860,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E>
// Read in all elements and place in queue
for (;;) {
@SuppressWarnings("unchecked")
E item = (E)s.readObject();
if (item == null)
break;

View File

@ -44,15 +44,11 @@ public class AccessibleResourceBundle extends ListResourceBundle {
* localized display strings.
*/
public Object[][] getContents() {
return contents;
}
// The table holding the mapping between the programmatic keys
// and the display strings for the en_US locale.
return new Object[][] {
/**
* The table holding the mapping between the programmatic keys
* and the display strings for the en_US locale.
*/
static final Object[][] contents = {
// LOCALIZE THIS
// LOCALIZE THIS
// Role names
// { "application","application" },
// { "border","border" },
@ -151,5 +147,6 @@ public class AccessibleResourceBundle extends ListResourceBundle {
{ "vertical","vertical" },
{ "horizontal","horizontal" }
// END OF MATERIAL TO LOCALIZE
};
};
}
}

View File

@ -78,7 +78,7 @@ public class BMPImageWriteParam extends ImageWriteParam {
super(locale);
// Set compression types ("BI_RGB" denotes uncompressed).
compressionTypes = BMPConstants.compressionTypeNames;
compressionTypes = BMPConstants.compressionTypeNames.clone();
// Set compression flag.
canWriteCompressed = true;

View File

@ -77,7 +77,10 @@ public abstract class ImageReaderSpi extends ImageReaderWriterSpi {
* A single-element array, initially containing
* <code>ImageInputStream.class</code>, to be returned from
* <code>getInputTypes</code>.
* @deprecated Instead of using this field, directly create
* the equivalent array <code>{ ImageInputStream.class }<code>.
*/
@Deprecated
public static final Class[] STANDARD_INPUT_TYPE =
{ ImageInputStream.class };
@ -227,7 +230,11 @@ public abstract class ImageReaderSpi extends ImageReaderWriterSpi {
throw new IllegalArgumentException
("inputTypes.length == 0!");
}
this.inputTypes = (Class[])inputTypes.clone();
this.inputTypes = (inputTypes == STANDARD_INPUT_TYPE) ?
new Class<?>[] { ImageInputStream.class } :
inputTypes.clone();
// If length == 0, leave it null
if (writerSpiNames != null && writerSpiNames.length > 0) {
this.writerSpiNames = (String[])writerSpiNames.clone();

View File

@ -77,9 +77,12 @@ public abstract class ImageWriterSpi extends ImageReaderWriterSpi {
/**
* A single-element array, initially containing
* <code>ImageInputStream.class</code>, to be returned from
* <code>getInputTypes</code>.
* <code>ImageOutputStream.class</code>, to be returned from
* <code>getOutputTypes</code>.
* @deprecated Instead of using this field, directly create
* the equivalent array <code>{ ImageOutputStream.class }<code>.
*/
@Deprecated
public static final Class[] STANDARD_OUTPUT_TYPE =
{ ImageOutputStream.class };
@ -228,7 +231,11 @@ public abstract class ImageWriterSpi extends ImageReaderWriterSpi {
throw new IllegalArgumentException
("outputTypes.length == 0!");
}
this.outputTypes = (Class[])outputTypes.clone();
this.outputTypes = (outputTypes == STANDARD_OUTPUT_TYPE) ?
new Class<?>[] { ImageOutputStream.class } :
outputTypes.clone();
// If length == 0, leave it null
if (readerSpiNames != null && readerSpiNames.length > 0) {
this.readerSpiNames = (String[])readerSpiNames.clone();

View File

@ -62,6 +62,10 @@ public class FileCacheImageInputStream extends ImageInputStreamImpl {
/** The DisposerRecord that closes the underlying cache. */
private final DisposerRecord disposerRecord;
/** The CloseAction that closes the stream in
* the StreamCloser's shutdown hook */
private final StreamCloser.CloseAction closeAction;
/**
* Constructs a <code>FileCacheImageInputStream</code> that will read
* from a given <code>InputStream</code>.
@ -96,7 +100,9 @@ public class FileCacheImageInputStream extends ImageInputStreamImpl {
this.cacheFile =
File.createTempFile("imageio", ".tmp", cacheDir);
this.cache = new RandomAccessFile(cacheFile, "rw");
StreamCloser.addToQueue(this);
this.closeAction = StreamCloser.createCloseAction(this);
StreamCloser.addToQueue(closeAction);
disposerRecord = new StreamDisposerRecord(cacheFile, cache);
if (getClass() == FileCacheImageInputStream.class) {
@ -242,7 +248,7 @@ public class FileCacheImageInputStream extends ImageInputStreamImpl {
stream = null;
cache = null;
cacheFile = null;
StreamCloser.removeFromQueue(this);
StreamCloser.removeFromQueue(closeAction);
}
/**

View File

@ -48,6 +48,10 @@ public class FileCacheImageOutputStream extends ImageOutputStreamImpl {
// Pos after last (rightmost) byte written
private long maxStreamPos = 0L;
/** The CloseAction that closes the stream in
* the StreamCloser's shutdown hook */
private final StreamCloser.CloseAction closeAction;
/**
* Constructs a <code>FileCacheImageOutputStream</code> that will write
* to a given <code>outputStream</code>.
@ -82,7 +86,9 @@ public class FileCacheImageOutputStream extends ImageOutputStreamImpl {
this.cacheFile =
File.createTempFile("imageio", ".tmp", cacheDir);
this.cache = new RandomAccessFile(cacheFile, "rw");
StreamCloser.addToQueue(this);
this.closeAction = StreamCloser.createCloseAction(this);
StreamCloser.addToQueue(closeAction);
}
public int read() throws IOException {
@ -227,7 +233,7 @@ public class FileCacheImageOutputStream extends ImageOutputStreamImpl {
cacheFile = null;
stream.flush();
stream = null;
StreamCloser.removeFromQueue(this);
StreamCloser.removeFromQueue(closeAction);
}
public void flushBefore(long pos) throws IOException {

View File

@ -690,7 +690,7 @@ public class OpenMBeanAttributeInfoSupport
private static <T> T convertFromString(String s, OpenType<T> openType) {
Class<T> c;
try {
c = cast(Class.forName(openType.getClassName()));
c = cast(Class.forName(openType.safeGetClassName()));
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.toString()); // can't happen
}
@ -711,7 +711,7 @@ public class OpenMBeanAttributeInfoSupport
} catch (Exception e) {
final String msg =
"Could not convert \"" + s + "\" using method: " + valueOf;
throw new IllegalArgumentException(msg);
throw new IllegalArgumentException(msg, e);
}
}
@ -728,7 +728,7 @@ public class OpenMBeanAttributeInfoSupport
} catch (Exception e) {
final String msg =
"Could not convert \"" + s + "\" using constructor: " + con;
throw new IllegalArgumentException(msg);
throw new IllegalArgumentException(msg, e);
}
}
@ -757,7 +757,7 @@ public class OpenMBeanAttributeInfoSupport
stringArrayClass =
Class.forName(squareBrackets + "Ljava.lang.String;");
targetArrayClass =
Class.forName(squareBrackets + "L" + baseType.getClassName() +
Class.forName(squareBrackets + "L" + baseType.safeGetClassName() +
";");
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.toString()); // can't happen

View File

@ -304,7 +304,12 @@ public abstract class OpenType<T> implements Serializable {
* @return the class name.
*/
public String getClassName() {
return className;
}
// A version of getClassName() that can only be called from within this
// package and that cannot be overridden.
String safeGetClassName() {
return className;
}

View File

@ -0,0 +1,788 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package javax.swing;
import javax.swing.plaf.LayerUI;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* {@code JLayer} is a universal decorator for Swing components
* which enables you to implement various advanced painting effects as well as
* receive notifications of all {@code AWTEvent}s generated within its borders.
* <p/>
* {@code JLayer} delegates the handling of painting and input events to a
* {@link javax.swing.plaf.LayerUI} object, which performs the actual decoration.
* <p/>
* The custom painting implemented in the {@code LayerUI} and events notification
* work for the JLayer itself and all its subcomponents.
* This combination enables you to enrich existing components
* by adding new advanced functionality such as temporary locking of a hierarchy,
* data tips for compound components, enhanced mouse scrolling etc and so on.
* <p/>
* {@code JLayer} is a good solution if you only need to do custom painting
* over compound component or catch input events from its subcomponents.
* <pre>
* // create a component to be decorated with the layer
* JPanel panel = new JPanel();
* panel.add(new JButton("JButton"));
* // This custom layerUI will fill the layer with translucent green
* // and print out all mouseMotion events generated within its borders
* LayerUI&lt;JPanel&gt; layerUI = new LayerUI&lt;JPanel&gt;() {
* public void paint(Graphics g, JCompo nent c) {
* // paint the layer as is
* super.paint(g, c);
* // fill it with the translucent green
* g.setColor(new Color(0, 128, 0, 128));
* g.fillRect(0, 0, c.getWidth(), c.getHeight());
* }
* // overridden method which catches MouseMotion events
* public void eventDispatched(AWTEvent e, JLayer&lt;JPanel&gt; l) {
* System.out.println("AWTEvent detected: " + e);
* }
* };
* // create the layer for the panel using our custom layerUI
* JLayer&lt;JPanel&gt; layer = new JLayer&lt;JPanel&gt;(panel, layerUI);
* // work with the layer as with any other Swing component
* frame.add(layer);
* </pre>
*
* <b>Note:</b> {@code JLayer} doesn't support the following methods:
* <ul>
* <li>{@link Container#add(java.awt.Component)}</li>
* <li>{@link Container#add(String, java.awt.Component)}</li>
* <li>{@link Container#add(java.awt.Component, int)}</li>
* <li>{@link Container#add(java.awt.Component, Object)}</li>
* <li>{@link Container#add(java.awt.Component, Object, int)}</li>
* </ul>
* using any of of them will cause {@code UnsupportedOperationException} to be thrown,
* to add a component to {@code JLayer}
* use {@link #setView(Component)} or {@link #setGlassPane(JPanel)}.
*
* @param <V> the type of {@code JLayer}'s view component
*
* @see #JLayer(Component)
* @see #setView(Component)
* @see #getView()
* @see javax.swing.plaf.LayerUI
* @see #JLayer(Component, LayerUI)
* @see #setUI(javax.swing.plaf.LayerUI)
* @see #getUI()
* @since 1.7
*
* @author Alexander Potochkin
*/
public final class JLayer<V extends Component>
extends JComponent
implements Scrollable, PropertyChangeListener {
private V view;
// this field is necessary because JComponent.ui is transient
// when layerUI is serializable
private LayerUI<? super V> layerUI;
private JPanel glassPane;
private boolean isPainting;
private static final DefaultLayerLayout sharedLayoutInstance =
new DefaultLayerLayout();
private long eventMask;
private static final LayerEventController eventController =
new LayerEventController();
private static final long ACCEPTED_EVENTS =
AWTEvent.COMPONENT_EVENT_MASK |
AWTEvent.CONTAINER_EVENT_MASK |
AWTEvent.FOCUS_EVENT_MASK |
AWTEvent.KEY_EVENT_MASK |
AWTEvent.MOUSE_WHEEL_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK |
AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.INPUT_METHOD_EVENT_MASK |
AWTEvent.HIERARCHY_EVENT_MASK |
AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK;
/**
* Creates a new {@code JLayer} object with a {@code null} view component
* and {@code null} {@link javax.swing.plaf.LayerUI}.
*
* @see #setView
* @see #setUI
*/
public JLayer() {
this(null);
}
/**
* Creates a new {@code JLayer} object
* with {@code null} {@link javax.swing.plaf.LayerUI}.
*
* @param view the component to be decorated by this {@code JLayer}
*
* @see #setUI
*/
public JLayer(V view) {
this(view, null);
}
/**
* Creates a new {@code JLayer} object with the specified view component
* and {@link javax.swing.plaf.LayerUI} object.
*
* @param view the component to be decorated
* @param ui the {@link javax.swing.plaf.LayerUI} delegate
* to be used by this {@code JLayer}
*/
public JLayer(V view, LayerUI<V> ui) {
setLayout(sharedLayoutInstance);
setGlassPane(createGlassPane());
setView(view);
setUI(ui);
}
/**
* Returns the {@code JLayer}'s view component or {@code null}.
* <br/>This is a bound property.
*
* @return the {@code JLayer}'s view component
* or {@code null} if none exists
*
* @see #setView(V)
*/
public V getView() {
return view;
}
/**
* Sets the {@code JLayer}'s view component, which can be {@code null}.
* <br/>This is a bound property.
*
* @param view the view component for this {@code JLayer}
*
* @see #getView()
*/
public void setView(V view) {
Component oldView = getView();
if (oldView != null) {
super.remove(oldView);
}
if (view != null) {
super.addImpl(view, null, getComponentCount());
}
this.view = view;
firePropertyChange("view", oldView, view);
revalidate();
repaint();
}
/**
* Sets the {@link javax.swing.plaf.LayerUI} which will perform painting
* and receive input events for this {@code JLayer}.
*
* @param ui the {@link javax.swing.plaf.LayerUI} for this {@code JLayer}
*/
public void setUI(LayerUI<? super V> ui) {
this.layerUI = ui;
super.setUI(ui);
}
/**
* Returns the {@link javax.swing.plaf.LayerUI} for this {@code JLayer}.
*
* @return the {@code LayerUI} for this {@code JLayer}
*/
public LayerUI<? super V> getUI() {
return layerUI;
}
/**
* Returns the {@code JLayer}'s glassPane component or {@code null}.
* <br/>This is a bound property.
*
* @return the {@code JLayer}'s glassPane component
* or {@code null} if none exists
*
* @see #setGlassPane(JPanel)
*/
public JPanel getGlassPane() {
return glassPane;
}
/**
* Sets the {@code JLayer}'s glassPane component, which can be {@code null}.
* <br/>This is a bound property.
*
* @param glassPane the glassPane component of this {@code JLayer}
*
* @see #getGlassPane()
*/
public void setGlassPane(JPanel glassPane) {
Component oldGlassPane = getGlassPane();
if (oldGlassPane != null) {
super.remove(oldGlassPane);
}
if (glassPane != null) {
super.addImpl(glassPane, null, 0);
}
this.glassPane = glassPane;
firePropertyChange("glassPane", oldGlassPane, glassPane);
revalidate();
repaint();
}
/**
* Called by the constructor methods to create a default {@code glassPane}.
* By default this method creates a new JPanel with visibility set to true
* and opacity set to false.
*
* @return the default {@code glassPane}
*/
public JPanel createGlassPane() {
return new DefaultLayerGlassPane();
}
/**
* This method is not supported by {@code JLayer}
* and always throws {@code UnsupportedOperationException}
*
* @throws UnsupportedOperationException this method is not supported
*
* @see #setView(Component)
* @see #setGlassPane(Component)
*/
protected void addImpl(Component comp, Object constraints, int index) {
throw new UnsupportedOperationException(
"Adding components to JLayer is not supported, " +
"use setView() or setGlassPane() instead");
}
/**
* {@inheritDoc}
*/
public void remove(Component comp) {
if (comp == getView()) {
setView(null);
} else if (comp == getGlassPane()) {
setGlassPane(null);
} else {
super.remove(comp);
}
}
/**
* {@inheritDoc}
*/
public void removeAll() {
setView(null);
setGlassPane(null);
}
/**
* Delegates all painting to the {@link javax.swing.plaf.LayerUI} object.
*
* @param g the {@code Graphics} to render to
*/
public void paint(Graphics g) {
if (!isPainting) {
isPainting = true;
super.paintComponent(g);
isPainting = false;
} else {
super.paint(g);
}
}
/**
* This method is empty, because all painting is done by
* {@link #paint(Graphics)} and
* {@link javax.swing.plaf.LayerUI#update(Graphics, JComponent)} methods
*/
protected void paintComponent(Graphics g) {
}
/**
* To enable the correct painting of the {@code glassPane} and view component,
* the {@code JLayer} overrides the default implementation of
* this method to return {@code false} when the {@code glassPane} is visible.
*
* @return false if {@code JLayer}'s {@code glassPane} is visible
*/
public boolean isOptimizedDrawingEnabled() {
return !glassPane.isVisible();
}
/**
* {@inheritDoc}
*/
public void propertyChange(PropertyChangeEvent evt) {
if (getUI() != null) {
getUI().applyPropertyChange(evt, this);
}
}
/**
* Sets the bitmask of event types to receive by this {@code JLayer}.
* Here is the list of the supported event types:
* <ul>
* <li>AWTEvent.COMPONENT_EVENT_MASK</li>
* <li>AWTEvent.CONTAINER_EVENT_MASK</li>
* <li>AWTEvent.FOCUS_EVENT_MASK</li>
* <li>AWTEvent.KEY_EVENT_MASK</li>
* <li>AWTEvent.MOUSE_WHEEL_EVENT_MASK</li>
* <li>AWTEvent.MOUSE_MOTION_EVENT_MASK</li>
* <li>AWTEvent.MOUSE_EVENT_MASK</li>
* <li>AWTEvent.INPUT_METHOD_EVENT_MASK</li>
* <li>AWTEvent.HIERARCHY_EVENT_MASK</li>
* <li>AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK</li>
* </ul>
* <p/>
* If {@code LayerUI} is installed,
* {@link javax.swing.plaf.LayerUI#eventDispatched(AWTEvent, JLayer)} method
* will only receive events that match the event mask.
* <p/>
* The following example shows how to correclty use this method
* in the {@code LayerUI} implementations:
* <pre>
* public void installUI(JComponent c) {
* super.installUI(c);
* JLayer l = (JLayer) c;
* // this LayerUI will receive only key and focus events
* l.setLayerEventMask(AWTEvent.KEY_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
* }
*
* public void uninstallUI(JComponent c) {
* super.uninstallUI(c);
* JLayer l = (JLayer) c;
* // JLayer must be returned to its initial state
* l.setLayerEventMask(0);
* }
* </pre>
*
* By default {@code JLayer} receives no events.
*
* @param layerEventMask the bitmask of event types to receive
*
* @throws IllegalArgumentException if the {@code layerEventMask} parameter
* contains unsupported event types
* @see #getLayerEventMask()
*/
public void setLayerEventMask(long layerEventMask) {
if (layerEventMask != (layerEventMask & ACCEPTED_EVENTS)) {
throw new IllegalArgumentException(
"The event bitmask contains unsupported event types");
}
long oldEventMask = getLayerEventMask();
this.eventMask = layerEventMask;
firePropertyChange("layerEventMask", oldEventMask, layerEventMask);
if (layerEventMask != oldEventMask) {
disableEvents(oldEventMask);
enableEvents(eventMask);
eventController.updateAWTEventListener(this);
}
}
/**
* Returns the bitmap of event mask to receive by this {@code JLayer}
* and its {@code LayerUI}.
* <p/>
* It means that {@link javax.swing.plaf.LayerUI#eventDispatched(AWTEvent, JLayer)} method
* will only receive events that match the event mask.
* <p/>
* By default {@code JLayer} receives no events.
*
* @return the bitmask of event types to receive for this {@code JLayer}
*/
public long getLayerEventMask() {
return eventMask;
}
/**
* Delegates its functionality to the {@link javax.swing.plaf.LayerUI#updateUI(JLayer)} method,
* if {@code LayerUI} is set.
*/
public void updateUI() {
if (getUI() != null) {
getUI().updateUI(this);
}
}
/**
* Returns the preferred size of the viewport for a view component.
* <p/>
* If the ui delegate of this layer is not {@code null}, this method delegates its
* implementation to the {@code LayerUI.getPreferredScrollableViewportSize(JLayer)}
*
* @return the preferred size of the viewport for a view component
*
* @see Scrollable
* @see LayerUI#getPreferredScrollableViewportSize(JLayer)
*/
public Dimension getPreferredScrollableViewportSize() {
if (getUI() != null) {
return getUI().getPreferredScrollableViewportSize(this);
}
return getPreferredSize();
}
/**
* Returns a scroll increment, which is required for components
* that display logical rows or columns in order to completely expose
* one block of rows or columns, depending on the value of orientation.
* <p/>
* If the ui delegate of this layer is not {@code null}, this method delegates its
* implementation to the {@code LayerUI.getScrollableBlockIncrement(JLayer,Rectangle,int,int)}
*
* @return the "block" increment for scrolling in the specified direction
*
* @see Scrollable
* @see LayerUI#getScrollableBlockIncrement(JLayer, Rectangle, int, int)
*/
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction) {
if (getUI() != null) {
return getUI().getScrollableBlockIncrement(this, visibleRect,
orientation, direction);
}
return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
visibleRect.width;
}
/**
* Returns {@code false} to indicate that the height of the viewport does not
* determine the height of the layer, unless the preferred height
* of the layer is smaller than the height of the viewport.
* <p/>
* If the ui delegate of this layer is not null, this method delegates its
* implementation to the {@code LayerUI.getScrollableTracksViewportHeight(JLayer)}
*
* @return whether the layer should track the height of the viewport
*
* @see Scrollable
* @see LayerUI#getScrollableTracksViewportHeight(JLayer)
*/
public boolean getScrollableTracksViewportHeight() {
if (getUI() != null) {
return getUI().getScrollableTracksViewportHeight(this);
}
if (getParent() instanceof JViewport) {
return ((getParent()).getHeight() > getPreferredSize().height);
}
return false;
}
/**
* Returns {@code false} to indicate that the width of the viewport does not
* determine the width of the layer, unless the preferred width
* of the layer is smaller than the width of the viewport.
* <p/>
* If the ui delegate of this layer is not null, this method delegates its
* implementation to the {@code LayerUI.getScrollableTracksViewportWidth(JLayer)}
*
* @return whether the layer should track the width of the viewport
*
* @see Scrollable
* @see LayerUI#getScrollableTracksViewportWidth(JLayer)
*/
public boolean getScrollableTracksViewportWidth() {
if (getUI() != null) {
return getUI().getScrollableTracksViewportWidth(this);
}
if (getParent() instanceof JViewport) {
return ((getParent()).getWidth() > getPreferredSize().width);
}
return false;
}
/**
* Returns a scroll increment, which is required for components
* that display logical rows or columns in order to completely expose
* one new row or column, depending on the value of orientation.
* Ideally, components should handle a partially exposed row or column
* by returning the distance required to completely expose the item.
* <p/>
* Scrolling containers, like {@code JScrollPane}, will use this method
* each time the user requests a unit scroll.
* <p/>
* If the ui delegate of this layer is not {@code null}, this method delegates its
* implementation to the {@code LayerUI.getScrollableUnitIncrement(JLayer,Rectangle,int,int)}
*
* @return The "unit" increment for scrolling in the specified direction.
* This value should always be positive.
*
* @see Scrollable
* @see LayerUI#getScrollableUnitIncrement(JLayer, Rectangle, int, int)
*/
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
int direction) {
if (getUI() != null) {
return getUI().getScrollableUnitIncrement(
this, visibleRect, orientation, direction);
}
return 1;
}
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
if (getUI() != null) {
setUI(getUI());
}
if (getLayerEventMask() != 0) {
eventController.updateAWTEventListener(this);
}
}
/**
* static AWTEventListener to be shared with all AbstractLayerUIs
*/
private static class LayerEventController implements AWTEventListener {
private ArrayList<WeakReference<JLayer>> layerList =
new ArrayList<WeakReference<JLayer>>();
private long currentEventMask;
@SuppressWarnings("unchecked")
public void eventDispatched(AWTEvent event) {
Object source = event.getSource();
if (source instanceof Component) {
Component component = (Component) source;
while (component != null) {
if (component instanceof JLayer) {
JLayer l = (JLayer) component;
LayerUI ui = l.getUI();
if (ui != null &&
isEventEnabled(l.getLayerEventMask(),
event.getID())) {
ui.eventDispatched(event, l);
}
}
component = component.getParent();
}
}
}
private boolean layerListContains(JLayer l) {
for (WeakReference<JLayer> layerWeakReference : layerList) {
if (layerWeakReference.get() == l) {
return true;
}
}
return false;
}
private void updateAWTEventListener(JLayer layer) {
if (!layerListContains(layer) && layer.getLayerEventMask() != 0) {
layerList.add(new WeakReference<JLayer>(layer));
}
long combinedMask = 0;
Iterator<WeakReference<JLayer>> it = layerList.iterator();
while (it.hasNext()) {
WeakReference<JLayer> weakRef = it.next();
JLayer currLayer = weakRef.get();
if (currLayer == null) {
it.remove();
} else {
combinedMask |= currLayer.getLayerEventMask();
}
}
if (combinedMask == 0) {
removeAWTEventListener();
layerList.clear();
} else if (getCurrentEventMask() != combinedMask) {
removeAWTEventListener();
addAWTEventListener(combinedMask);
}
}
private long getCurrentEventMask() {
return currentEventMask;
}
private void addAWTEventListener(final long eventMask) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
Toolkit.getDefaultToolkit().
addAWTEventListener(LayerEventController.this, eventMask);
return null;
}
});
currentEventMask = eventMask;
}
private void removeAWTEventListener() {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
Toolkit.getDefaultToolkit().
removeAWTEventListener(LayerEventController.this);
return null;
}
});
currentEventMask = 0;
}
private boolean isEventEnabled(long eventMask, int id) {
return (((eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0 &&
id >= ComponentEvent.COMPONENT_FIRST &&
id <= ComponentEvent.COMPONENT_LAST)
|| ((eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0 &&
id >= ContainerEvent.CONTAINER_FIRST &&
id <= ContainerEvent.CONTAINER_LAST)
|| ((eventMask & AWTEvent.FOCUS_EVENT_MASK) != 0 &&
id >= FocusEvent.FOCUS_FIRST &&
id <= FocusEvent.FOCUS_LAST)
|| ((eventMask & AWTEvent.KEY_EVENT_MASK) != 0 &&
id >= KeyEvent.KEY_FIRST &&
id <= KeyEvent.KEY_LAST)
|| ((eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0 &&
id == MouseEvent.MOUSE_WHEEL)
|| ((eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0 &&
(id == MouseEvent.MOUSE_MOVED ||
id == MouseEvent.MOUSE_DRAGGED))
|| ((eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0 &&
id != MouseEvent.MOUSE_MOVED &&
id != MouseEvent.MOUSE_DRAGGED &&
id != MouseEvent.MOUSE_WHEEL &&
id >= MouseEvent.MOUSE_FIRST &&
id <= MouseEvent.MOUSE_LAST)
|| ((eventMask & AWTEvent.INPUT_METHOD_EVENT_MASK) != 0 &&
id >= InputMethodEvent.INPUT_METHOD_FIRST &&
id <= InputMethodEvent.INPUT_METHOD_LAST)
|| ((eventMask & AWTEvent.HIERARCHY_EVENT_MASK) != 0 &&
id == HierarchyEvent.HIERARCHY_CHANGED)
|| ((eventMask & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) != 0 &&
(id == HierarchyEvent.ANCESTOR_MOVED ||
id == HierarchyEvent.ANCESTOR_RESIZED)));
}
}
/**
* The default glassPane for the {@link javax.swing.JLayer}.
* It is a subclass of {@code JPanel} which is non opaque by default.
*/
private static class DefaultLayerGlassPane extends JPanel {
/**
* Creates a new {@link DefaultLayerGlassPane}
*/
public DefaultLayerGlassPane() {
setOpaque(false);
}
/**
* First, implementatation of this method iterates through
* glassPane's child components and returns {@code true}
* if any of them is visible and contains passed x,y point.
* After that it checks if no mouseListeners is attached to this component
* and no mouse cursor is set, then it returns {@code false},
* otherwise calls the super implementation of this method.
*
* @param x the <i>x</i> coordinate of the point
* @param y the <i>y</i> coordinate of the point
* @return true if this component logically contains x,y
*/
public boolean contains(int x, int y) {
for (int i = 0; i < getComponentCount(); i++) {
Component c = getComponent(i);
Point point = SwingUtilities.convertPoint(this, new Point(x, y), c);
if(c.isVisible() && c.contains(point)){
return true;
}
}
if (getMouseListeners().length == 0
&& getMouseMotionListeners().length == 0
&& getMouseWheelListeners().length == 0
&& !isCursorSet()) {
return false;
}
return super.contains(x, y);
}
}
/**
* The default layout manager for the {@link javax.swing.JLayer}.<br/>
* It places the glassPane on top of the view component
* and makes it the same size as {@code JLayer},
* it also makes the view component the same size but minus layer's insets<br/>
*/
private static class DefaultLayerLayout implements LayoutManager, Serializable {
/**
* {@inheritDoc}
*/
public void layoutContainer(Container parent) {
JLayer layer = (JLayer) parent;
Component view = layer.getView();
Component glassPane = layer.getGlassPane();
if (view != null) {
Insets insets = layer.getInsets();
view.setLocation(insets.left, insets.top);
view.setSize(layer.getWidth() - insets.left - insets.right,
layer.getHeight() - insets.top - insets.bottom);
}
if (glassPane != null) {
glassPane.setLocation(0, 0);
glassPane.setSize(layer.getWidth(), layer.getHeight());
}
}
/**
* {@inheritDoc}
*/
public Dimension minimumLayoutSize(Container parent) {
JLayer layer = (JLayer) parent;
Insets insets = layer.getInsets();
Dimension ret = new Dimension(insets.left + insets.right,
insets.top + insets.bottom);
Component view = layer.getView();
if (view != null) {
Dimension size = view.getMinimumSize();
ret.width += size.width;
ret.height += size.height;
}
if (ret.width == 0 || ret.height == 0) {
ret.width = ret.height = 4;
}
return ret;
}
/**
* {@inheritDoc}
*/
public Dimension preferredLayoutSize(Container parent) {
JLayer layer = (JLayer) parent;
Insets insets = layer.getInsets();
Dimension ret = new Dimension(insets.left + insets.right,
insets.top + insets.bottom);
Component view = layer.getView();
if (view != null) {
Dimension size = view.getPreferredSize();
if (size.width > 0 && size.height > 0) {
ret.width += size.width;
ret.height += size.height;
}
}
return ret;
}
/**
* {@inheritDoc}
*/
public void addLayoutComponent(String name, Component comp) {
}
/**
* {@inheritDoc}
*/
public void removeLayoutComponent(Component comp) {
}
}
}

View File

@ -37,6 +37,8 @@ import java.util.Vector;
import java.lang.ref.WeakReference;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.awt.shell.*;
@ -718,8 +720,13 @@ class WindowsFileSystemView extends FileSystemView {
return isFileSystemRoot(dir);
}
public boolean isFloppyDrive(File dir) {
String path = dir.getAbsolutePath();
public boolean isFloppyDrive(final File dir) {
String path = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return dir.getAbsolutePath();
}
});
return (path != null && (path.equals("A:\\") || path.equals("B:\\")));
}

View File

@ -0,0 +1,370 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package javax.swing.plaf;
import javax.accessibility.Accessible;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
/**
* The base class for all {@link javax.swing.JLayer}'s UI delegates.
* <p/>
* {@link #paint(java.awt.Graphics, javax.swing.JComponent)} method performes the
* painting of the {@code JLayer}
* and {@link #eventDispatched(AWTEvent, JLayer)} method is notified
* about any {@code AWTEvent}s which have been generated by a {@code JLayer}
* or any of its subcomponents.
* <p/>
* The {@code LayerUI} differs from the UI delegates of the other components,
* because it is LookAndFeel independent and is not updated by default when
* the system LookAndFeel is changed.
* <p/>
* The subclasses of {@code LayerUI} can either be stateless and shareable
* by multiple {@code JLayer}s or not shareable.
*
* @param <V> one of the super types of {@code JLayer}'s view component
*
* @see JLayer#setUI(LayerUI)
* @see JLayer#setView(Component)
* @see JLayer#getView()
* @since 1.7
*
* @author Alexander Potochkin
*/
public class LayerUI<V extends Component>
extends ComponentUI implements Serializable {
private final PropertyChangeSupport propertyChangeSupport =
new PropertyChangeSupport(this);
/**
* Paints the specified component.
* Subclasses should override this method and use
* the specified {@code Graphics} object to
* render the content of the component.
*
* @param g the {@code Graphics} context in which to paint;
* @param c the component being painted;
* it can be safely cast to the {@code JLayer<V>}
*/
@Override
public void paint(Graphics g, JComponent c) {
c.paint(g);
}
/**
* Dispatches {@code AWTEvent}s for {@code JLayer}
* and <b>all it subcomponents</b> to this {@code LayerUI} instance.
* <p>
* To enable the {@code AWTEvent} of the particular type,
* you call {@link javax.swing.JLayer#setLayerEventMask}
* in {@link #installUI(javax.swing.JComponent)}
* and set the layer event mask to {@code 0}
* in {@link #uninstallUI(javax.swing.JComponent)} after that
*
* @param e the event to be dispatched
* @param l the layer this LayerUI is set to
*
* @see JLayer#setLayerEventMask(long)
* @see javax.swing.JLayer#getLayerEventMask()
*/
public void eventDispatched(AWTEvent e, JLayer<? extends V> l){
}
/**
* Invoked when {@link javax.swing.JLayer#updateUI()} is called
* by the {@code JLayer} this {@code LayerUI} is set to.
*
* @param l the {@code JLayer} which UI is updated
*/
public void updateUI(JLayer<? extends V> l){
}
/**
* Configures the {@code JLayer} this {@code LayerUI} is set to.
* The default implementation registers the {@code LayerUI}
* as a property change listener for the passed {@code JLayer} component.
*
* @param c the {@code JLayer} component where this UI delegate is being installed
*/
public void installUI(JComponent c) {
addPropertyChangeListener((JLayer) c);
}
/**
* Reverses the configuration which was previously set
* in the {@link #installUI(JComponent)} method.
* The default implementation unregisters the property change listener
* for the passed JLayer component.
*
* @param c the component from which this UI delegate is being removed.
*/
public void uninstallUI(JComponent c) {
removePropertyChangeListener((JLayer) c);
}
/**
* Adds a PropertyChangeListener to the listener list. The listener is
* registered for all bound properties of this class.
* <p/>
* If {@code listener} is {@code null},
* no exception is thrown and no action is performed.
*
* @param listener the property change listener to be added
* @see #removePropertyChangeListener
* @see #getPropertyChangeListeners
* @see #addPropertyChangeListener(String, java.beans.PropertyChangeListener)
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
/**
* Removes a PropertyChangeListener from the listener list. This method
* should be used to remove PropertyChangeListeners that were registered
* for all bound properties of this class.
* <p/>
* If {@code listener} is {@code null},
* no exception is thrown and no action is performed.
*
* @param listener the PropertyChangeListener to be removed
* @see #addPropertyChangeListener
* @see #getPropertyChangeListeners
* @see #removePropertyChangeListener(String, PropertyChangeListener)
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
/**
* Returns an array of all the property change listeners
* registered on this component.
*
* @return all of this ui's {@code PropertyChangeListener}s
* or an empty array if no property change
* listeners are currently registered
* @see #addPropertyChangeListener
* @see #removePropertyChangeListener
* @see #getPropertyChangeListeners(String)
*/
public PropertyChangeListener[] getPropertyChangeListeners() {
return propertyChangeSupport.getPropertyChangeListeners();
}
/**
* Adds a PropertyChangeListener to the listener list for a specific
* property.
* <p/>
* If {@code propertyName} or {@code listener} is {@code null},
* no exception is thrown and no action is taken.
*
* @param propertyName one of the property names listed above
* @param listener the property change listener to be added
* @see #removePropertyChangeListener(String, PropertyChangeListener)
* @see #getPropertyChangeListeners(String)
* @see #addPropertyChangeListener(String, PropertyChangeListener)
*/
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
/**
* Removes a {@code PropertyChangeListener} from the listener
* list for a specific property. This method should be used to remove
* {@code PropertyChangeListener}s
* that were registered for a specific bound property.
* <p/>
* If {@code propertyName} or {@code listener} is {@code null},
* no exception is thrown and no action is taken.
*
* @param propertyName a valid property name
* @param listener the PropertyChangeListener to be removed
* @see #addPropertyChangeListener(String, PropertyChangeListener)
* @see #getPropertyChangeListeners(String)
* @see #removePropertyChangeListener(PropertyChangeListener)
*/
public void removePropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
}
/**
* Returns an array of all the listeners which have been associated
* with the named property.
*
* @return all of the {@code PropertyChangeListener}s associated with
* the named property; if no such listeners have been added or
* if {@code propertyName} is {@code null}, an empty
* array is returned
* @see #addPropertyChangeListener(String, PropertyChangeListener)
* @see #removePropertyChangeListener(String, PropertyChangeListener)
* @see #getPropertyChangeListeners
*/
public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
return propertyChangeSupport.getPropertyChangeListeners(propertyName);
}
/**
* Support for reporting bound property changes for Object properties.
* This method can be called when a bound property has changed and it will
* send the appropriate PropertyChangeEvent to any registered
* PropertyChangeListeners.
*
* @param propertyName the property whose value has changed
* @param oldValue the property's previous value
* @param newValue the property's new value
*/
protected void firePropertyChange(String propertyName,
Object oldValue, Object newValue) {
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
/**
* Notifies the {@code LayerUI} when any of its property are changed
* and enables updating every {@code JLayer} this {@code LayerUI} instance is set to.
*
* @param evt the PropertyChangeEvent generated by this {@code LayerUI}
* @param l the {@code JLayer} this LayerUI is set to
*/
public void applyPropertyChange(PropertyChangeEvent evt, JLayer<? extends V> l) {
}
/**
* Returns the preferred size of the viewport for a view component.
*
* @return the preferred size of the viewport for a view component
* @see Scrollable#getPreferredScrollableViewportSize()
*/
public Dimension getPreferredScrollableViewportSize(JLayer<? extends V> l) {
if (l.getView() instanceof Scrollable) {
return ((Scrollable)l.getView()).getPreferredScrollableViewportSize();
}
return l.getPreferredSize();
}
/**
* Returns a scroll increment, which is required for components
* that display logical rows or columns in order to completely expose
* one block of rows or columns, depending on the value of orientation.
*
* @return the "block" increment for scrolling in the specified direction
* @see Scrollable#getScrollableBlockIncrement(Rectangle, int, int)
*/
public int getScrollableBlockIncrement(JLayer<? extends V> l,
Rectangle visibleRect,
int orientation, int direction) {
if (l.getView() instanceof Scrollable) {
return ((Scrollable)l.getView()).getScrollableBlockIncrement(
visibleRect,orientation, direction);
}
return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
visibleRect.width;
}
/**
* Returns {@code false} to indicate that the height of the viewport does not
* determine the height of the layer, unless the preferred height
* of the layer is smaller than the height of the viewport.
*
* @return whether the layer should track the height of the viewport
* @see Scrollable#getScrollableTracksViewportHeight()
*/
public boolean getScrollableTracksViewportHeight(JLayer<? extends V> l) {
if (l.getView() instanceof Scrollable) {
return ((Scrollable)l.getView()).getScrollableTracksViewportHeight();
}
if (l.getParent() instanceof JViewport) {
return (((JViewport)l.getParent()).getHeight() > l.getPreferredSize().height);
}
return false;
}
/**
* Returns {@code false} to indicate that the width of the viewport does not
* determine the width of the layer, unless the preferred width
* of the layer is smaller than the width of the viewport.
*
* @return whether the layer should track the width of the viewport
* @see Scrollable
* @see LayerUI#getScrollableTracksViewportWidth(JLayer)
*/
public boolean getScrollableTracksViewportWidth(JLayer<? extends V> l) {
if (l.getView() instanceof Scrollable) {
return ((Scrollable)l.getView()).getScrollableTracksViewportWidth();
}
if (l.getParent() instanceof JViewport) {
return (((JViewport)l.getParent()).getWidth() > l.getPreferredSize().width);
}
return false;
}
/**
* Returns a scroll increment, which is required for components
* that display logical rows or columns in order to completely expose
* one new row or column, depending on the value of orientation.
* Ideally, components should handle a partially exposed row or column
* by returning the distance required to completely expose the item.
* <p>
* Scrolling containers, like JScrollPane, will use this method
* each time the user requests a unit scroll.
*
* @return The "unit" increment for scrolling in the specified direction.
* This value should always be positive.
* @see Scrollable#getScrollableUnitIncrement(Rectangle, int, int)
*/
public int getScrollableUnitIncrement(JLayer<? extends V> l,
Rectangle visibleRect,
int orientation, int direction) {
if (l.getView() instanceof Scrollable) {
return ((Scrollable)l.getView()).getScrollableUnitIncrement(
visibleRect, orientation, direction);
}
return 1;
}
/**
* If the {@code JLayer}'s view component is not {@code null},
* this calls the view's {@code getBaseline()} method.
* Otherwise, the default implementation is called.
*
* @param c {@code JLayer} to return baseline resize behavior for
* @param width the width to get the baseline for
* @param height the height to get the baseline for
* @return baseline or a value &lt; 0 indicating there is no reasonable
* baseline
*/
public int getBaseline(JComponent c, int width, int height) {
JLayer l = (JLayer) c;
if (l.getView() != null) {
return l.getView().getBaseline(width, height);
}
return super.getBaseline(c, width, height);
}
/**
* If the {@code JLayer}'s view component is not {@code null},
* this calls the view's {@code getBaselineResizeBehavior()} method.
* Otherwise, the default implementation is called.
*
* @param c {@code JLayer} to return baseline resize behavior for
* @return an enum indicating how the baseline changes as the component
* size changes
*/
public Component.BaselineResizeBehavior getBaselineResizeBehavior(JComponent c) {
JLayer l = (JLayer) c;
if (l.getView() != null) {
return l.getView().getBaselineResizeBehavior();
}
return super.getBaselineResizeBehavior(c);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2002-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -24,8 +24,13 @@
*/
package javax.swing.plaf.synth;
import javax.swing.*;
import java.util.*;
import sun.awt.AppContext;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.UIDefaults;
/**
* A distinct rendering area of a Swing component. A component may
@ -67,8 +72,8 @@ import java.util.*;
* @author Scott Violet
*/
public class Region {
private static final Map<String, Region> uiToRegionMap = new HashMap<String, Region>();
private static final Map<Region, String> lowerCaseNameMap = new HashMap<Region, String>();
private static final Object UI_TO_REGION_MAP_KEY = new Object();
private static final Object LOWER_CASE_NAME_MAP_KEY = new Object();
/**
* ArrowButton's are special types of buttons that also render a
@ -77,396 +82,433 @@ public class Region {
* To bind a style to this <code>Region</code> use the name
* <code>ArrowButton</code>.
*/
public static final Region ARROW_BUTTON = new Region("ArrowButton",
"ArrowButtonUI");
public static final Region ARROW_BUTTON = new Region("ArrowButton", false);
/**
* Button region. To bind a style to this <code>Region</code> use the name
* <code>Button</code>.
*/
public static final Region BUTTON = new Region("Button",
"ButtonUI");
public static final Region BUTTON = new Region("Button", false);
/**
* CheckBox region. To bind a style to this <code>Region</code> use the name
* <code>CheckBox</code>.
*/
public static final Region CHECK_BOX = new Region("CheckBox",
"CheckBoxUI");
public static final Region CHECK_BOX = new Region("CheckBox", false);
/**
* CheckBoxMenuItem region. To bind a style to this <code>Region</code> use
* the name <code>CheckBoxMenuItem</code>.
*/
public static final Region CHECK_BOX_MENU_ITEM = new Region(
"CheckBoxMenuItem", "CheckBoxMenuItemUI");
public static final Region CHECK_BOX_MENU_ITEM = new Region("CheckBoxMenuItem", false);
/**
* ColorChooser region. To bind a style to this <code>Region</code> use
* the name <code>ColorChooser</code>.
*/
public static final Region COLOR_CHOOSER = new Region(
"ColorChooser", "ColorChooserUI");
public static final Region COLOR_CHOOSER = new Region("ColorChooser", false);
/**
* ComboBox region. To bind a style to this <code>Region</code> use
* the name <code>ComboBox</code>.
*/
public static final Region COMBO_BOX = new Region(
"ComboBox", "ComboBoxUI");
public static final Region COMBO_BOX = new Region("ComboBox", false);
/**
* DesktopPane region. To bind a style to this <code>Region</code> use
* the name <code>DesktopPane</code>.
*/
public static final Region DESKTOP_PANE = new Region("DesktopPane",
"DesktopPaneUI");
public static final Region DESKTOP_PANE = new Region("DesktopPane", false);
/**
* DesktopIcon region. To bind a style to this <code>Region</code> use
* the name <code>DesktopIcon</code>.
*/
public static final Region DESKTOP_ICON = new Region("DesktopIcon",
"DesktopIconUI");
public static final Region DESKTOP_ICON = new Region("DesktopIcon", false);
/**
* EditorPane region. To bind a style to this <code>Region</code> use
* the name <code>EditorPane</code>.
*/
public static final Region EDITOR_PANE = new Region("EditorPane",
"EditorPaneUI");
public static final Region EDITOR_PANE = new Region("EditorPane", false);
/**
* FileChooser region. To bind a style to this <code>Region</code> use
* the name <code>FileChooser</code>.
*/
public static final Region FILE_CHOOSER = new Region("FileChooser",
"FileChooserUI");
public static final Region FILE_CHOOSER = new Region("FileChooser", false);
/**
* FormattedTextField region. To bind a style to this <code>Region</code> use
* the name <code>FormattedTextField</code>.
*/
public static final Region FORMATTED_TEXT_FIELD = new Region(
"FormattedTextField", "FormattedTextFieldUI");
public static final Region FORMATTED_TEXT_FIELD = new Region("FormattedTextField", false);
/**
* InternalFrame region. To bind a style to this <code>Region</code> use
* the name <code>InternalFrame</code>.
*/
public static final Region INTERNAL_FRAME = new Region("InternalFrame",
"InternalFrameUI");
public static final Region INTERNAL_FRAME = new Region("InternalFrame", false);
/**
* TitlePane of an InternalFrame. The TitlePane typically
* shows a menu, title, widgets to manipulate the internal frame.
* To bind a style to this <code>Region</code> use the name
* <code>InternalFrameTitlePane</code>.
*/
public static final Region INTERNAL_FRAME_TITLE_PANE =
new Region("InternalFrameTitlePane",
"InternalFrameTitlePaneUI");
public static final Region INTERNAL_FRAME_TITLE_PANE = new Region("InternalFrameTitlePane", false);
/**
* Label region. To bind a style to this <code>Region</code> use the name
* <code>Label</code>.
*/
public static final Region LABEL = new Region("Label", "LabelUI");
public static final Region LABEL = new Region("Label", false);
/**
* List region. To bind a style to this <code>Region</code> use the name
* <code>List</code>.
*/
public static final Region LIST = new Region("List", "ListUI");
public static final Region LIST = new Region("List", false);
/**
* Menu region. To bind a style to this <code>Region</code> use the name
* <code>Menu</code>.
*/
public static final Region MENU = new Region("Menu", "MenuUI");
public static final Region MENU = new Region("Menu", false);
/**
* MenuBar region. To bind a style to this <code>Region</code> use the name
* <code>MenuBar</code>.
*/
public static final Region MENU_BAR = new Region("MenuBar", "MenuBarUI");
public static final Region MENU_BAR = new Region("MenuBar", false);
/**
* MenuItem region. To bind a style to this <code>Region</code> use the name
* <code>MenuItem</code>.
*/
public static final Region MENU_ITEM = new Region("MenuItem","MenuItemUI");
public static final Region MENU_ITEM = new Region("MenuItem", false);
/**
* Accelerator region of a MenuItem. To bind a style to this
* <code>Region</code> use the name <code>MenuItemAccelerator</code>.
*/
public static final Region MENU_ITEM_ACCELERATOR = new Region(
"MenuItemAccelerator");
public static final Region MENU_ITEM_ACCELERATOR = new Region("MenuItemAccelerator", true);
/**
* OptionPane region. To bind a style to this <code>Region</code> use
* the name <code>OptionPane</code>.
*/
public static final Region OPTION_PANE = new Region("OptionPane",
"OptionPaneUI");
public static final Region OPTION_PANE = new Region("OptionPane", false);
/**
* Panel region. To bind a style to this <code>Region</code> use the name
* <code>Panel</code>.
*/
public static final Region PANEL = new Region("Panel", "PanelUI");
public static final Region PANEL = new Region("Panel", false);
/**
* PasswordField region. To bind a style to this <code>Region</code> use
* the name <code>PasswordField</code>.
*/
public static final Region PASSWORD_FIELD = new Region("PasswordField",
"PasswordFieldUI");
public static final Region PASSWORD_FIELD = new Region("PasswordField", false);
/**
* PopupMenu region. To bind a style to this <code>Region</code> use
* the name <code>PopupMenu</code>.
*/
public static final Region POPUP_MENU = new Region("PopupMenu",
"PopupMenuUI");
public static final Region POPUP_MENU = new Region("PopupMenu", false);
/**
* PopupMenuSeparator region. To bind a style to this <code>Region</code>
* use the name <code>PopupMenuSeparator</code>.
*/
public static final Region POPUP_MENU_SEPARATOR = new Region(
"PopupMenuSeparator", "PopupMenuSeparatorUI");
public static final Region POPUP_MENU_SEPARATOR = new Region("PopupMenuSeparator", false);
/**
* ProgressBar region. To bind a style to this <code>Region</code>
* use the name <code>ProgressBar</code>.
*/
public static final Region PROGRESS_BAR = new Region("ProgressBar",
"ProgressBarUI");
public static final Region PROGRESS_BAR = new Region("ProgressBar", false);
/**
* RadioButton region. To bind a style to this <code>Region</code>
* use the name <code>RadioButton</code>.
*/
public static final Region RADIO_BUTTON = new Region(
"RadioButton", "RadioButtonUI");
public static final Region RADIO_BUTTON = new Region("RadioButton", false);
/**
* RegionButtonMenuItem region. To bind a style to this <code>Region</code>
* use the name <code>RadioButtonMenuItem</code>.
*/
public static final Region RADIO_BUTTON_MENU_ITEM = new Region(
"RadioButtonMenuItem", "RadioButtonMenuItemUI");
public static final Region RADIO_BUTTON_MENU_ITEM = new Region("RadioButtonMenuItem", false);
/**
* RootPane region. To bind a style to this <code>Region</code> use
* the name <code>RootPane</code>.
*/
public static final Region ROOT_PANE = new Region("RootPane",
"RootPaneUI");
public static final Region ROOT_PANE = new Region("RootPane", false);
/**
* ScrollBar region. To bind a style to this <code>Region</code> use
* the name <code>ScrollBar</code>.
*/
public static final Region SCROLL_BAR = new Region("ScrollBar",
"ScrollBarUI");
public static final Region SCROLL_BAR = new Region("ScrollBar", false);
/**
* Track of the ScrollBar. To bind a style to this <code>Region</code> use
* the name <code>ScrollBarTrack</code>.
*/
public static final Region SCROLL_BAR_TRACK = new Region("ScrollBarTrack");
public static final Region SCROLL_BAR_TRACK = new Region("ScrollBarTrack", true);
/**
* Thumb of the ScrollBar. The thumb is the region of the ScrollBar
* that gives a graphical depiction of what percentage of the View is
* currently visible. To bind a style to this <code>Region</code> use
* the name <code>ScrollBarThumb</code>.
*/
public static final Region SCROLL_BAR_THUMB = new Region("ScrollBarThumb");
public static final Region SCROLL_BAR_THUMB = new Region("ScrollBarThumb", true);
/**
* ScrollPane region. To bind a style to this <code>Region</code> use
* the name <code>ScrollPane</code>.
*/
public static final Region SCROLL_PANE = new Region("ScrollPane",
"ScrollPaneUI");
public static final Region SCROLL_PANE = new Region("ScrollPane", false);
/**
* Separator region. To bind a style to this <code>Region</code> use
* the name <code>Separator</code>.
*/
public static final Region SEPARATOR = new Region("Separator",
"SeparatorUI");
public static final Region SEPARATOR = new Region("Separator", false);
/**
* Slider region. To bind a style to this <code>Region</code> use
* the name <code>Slider</code>.
*/
public static final Region SLIDER = new Region("Slider", "SliderUI");
public static final Region SLIDER = new Region("Slider", false);
/**
* Track of the Slider. To bind a style to this <code>Region</code> use
* the name <code>SliderTrack</code>.
*/
public static final Region SLIDER_TRACK = new Region("SliderTrack");
public static final Region SLIDER_TRACK = new Region("SliderTrack", true);
/**
* Thumb of the Slider. The thumb of the Slider identifies the current
* value. To bind a style to this <code>Region</code> use the name
* <code>SliderThumb</code>.
*/
public static final Region SLIDER_THUMB = new Region("SliderThumb");
public static final Region SLIDER_THUMB = new Region("SliderThumb", true);
/**
* Spinner region. To bind a style to this <code>Region</code> use the name
* <code>Spinner</code>.
*/
public static final Region SPINNER = new Region("Spinner", "SpinnerUI");
public static final Region SPINNER = new Region("Spinner", false);
/**
* SplitPane region. To bind a style to this <code>Region</code> use the name
* <code>SplitPane</code>.
*/
public static final Region SPLIT_PANE = new Region("SplitPane",
"SplitPaneUI");
public static final Region SPLIT_PANE = new Region("SplitPane", false);
/**
* Divider of the SplitPane. To bind a style to this <code>Region</code>
* use the name <code>SplitPaneDivider</code>.
*/
public static final Region SPLIT_PANE_DIVIDER = new Region(
"SplitPaneDivider");
public static final Region SPLIT_PANE_DIVIDER = new Region("SplitPaneDivider", true);
/**
* TabbedPane region. To bind a style to this <code>Region</code> use
* the name <code>TabbedPane</code>.
*/
public static final Region TABBED_PANE = new Region("TabbedPane",
"TabbedPaneUI");
public static final Region TABBED_PANE = new Region("TabbedPane", false);
/**
* Region of a TabbedPane for one tab. To bind a style to this
* <code>Region</code> use the name <code>TabbedPaneTab</code>.
*/
public static final Region TABBED_PANE_TAB = new Region("TabbedPaneTab");
public static final Region TABBED_PANE_TAB = new Region("TabbedPaneTab", true);
/**
* Region of a TabbedPane containing the tabs. To bind a style to this
* <code>Region</code> use the name <code>TabbedPaneTabArea</code>.
*/
public static final Region TABBED_PANE_TAB_AREA =
new Region("TabbedPaneTabArea");
public static final Region TABBED_PANE_TAB_AREA = new Region("TabbedPaneTabArea", true);
/**
* Region of a TabbedPane containing the content. To bind a style to this
* <code>Region</code> use the name <code>TabbedPaneContent</code>.
*/
public static final Region TABBED_PANE_CONTENT =
new Region("TabbedPaneContent");
public static final Region TABBED_PANE_CONTENT = new Region("TabbedPaneContent", true);
/**
* Table region. To bind a style to this <code>Region</code> use
* the name <code>Table</code>.
*/
public static final Region TABLE = new Region("Table", "TableUI");
public static final Region TABLE = new Region("Table", false);
/**
* TableHeader region. To bind a style to this <code>Region</code> use
* the name <code>TableHeader</code>.
*/
public static final Region TABLE_HEADER = new Region("TableHeader",
"TableHeaderUI");
public static final Region TABLE_HEADER = new Region("TableHeader", false);
/**
* TextArea region. To bind a style to this <code>Region</code> use
* the name <code>TextArea</code>.
*/
public static final Region TEXT_AREA = new Region("TextArea",
"TextAreaUI");
public static final Region TEXT_AREA = new Region("TextArea", false);
/**
* TextField region. To bind a style to this <code>Region</code> use
* the name <code>TextField</code>.
*/
public static final Region TEXT_FIELD = new Region("TextField",
"TextFieldUI");
public static final Region TEXT_FIELD = new Region("TextField", false);
/**
* TextPane region. To bind a style to this <code>Region</code> use
* the name <code>TextPane</code>.
*/
public static final Region TEXT_PANE = new Region("TextPane",
"TextPaneUI");
public static final Region TEXT_PANE = new Region("TextPane", false);
/**
* ToggleButton region. To bind a style to this <code>Region</code> use
* the name <code>ToggleButton</code>.
*/
public static final Region TOGGLE_BUTTON = new Region("ToggleButton",
"ToggleButtonUI");
public static final Region TOGGLE_BUTTON = new Region("ToggleButton", false);
/**
* ToolBar region. To bind a style to this <code>Region</code> use
* the name <code>ToolBar</code>.
*/
public static final Region TOOL_BAR = new Region("ToolBar", "ToolBarUI");
public static final Region TOOL_BAR = new Region("ToolBar", false);
/**
* Region of the ToolBar containing the content. To bind a style to this
* <code>Region</code> use the name <code>ToolBarContent</code>.
*/
public static final Region TOOL_BAR_CONTENT = new Region("ToolBarContent");
public static final Region TOOL_BAR_CONTENT = new Region("ToolBarContent", true);
/**
* Region for the Window containing the ToolBar. To bind a style to this
* <code>Region</code> use the name <code>ToolBarDragWindow</code>.
*/
public static final Region TOOL_BAR_DRAG_WINDOW = new Region(
"ToolBarDragWindow", null, false);
public static final Region TOOL_BAR_DRAG_WINDOW = new Region("ToolBarDragWindow", false);
/**
* ToolTip region. To bind a style to this <code>Region</code> use
* the name <code>ToolTip</code>.
*/
public static final Region TOOL_TIP = new Region("ToolTip", "ToolTipUI");
public static final Region TOOL_TIP = new Region("ToolTip", false);
/**
* ToolBar separator region. To bind a style to this <code>Region</code> use
* the name <code>ToolBarSeparator</code>.
*/
public static final Region TOOL_BAR_SEPARATOR = new Region(
"ToolBarSeparator", "ToolBarSeparatorUI");
public static final Region TOOL_BAR_SEPARATOR = new Region("ToolBarSeparator", false);
/**
* Tree region. To bind a style to this <code>Region</code> use the name
* <code>Tree</code>.
*/
public static final Region TREE = new Region("Tree", "TreeUI");
public static final Region TREE = new Region("Tree", false);
/**
* Region of the Tree for one cell. To bind a style to this
* <code>Region</code> use the name <code>TreeCell</code>.
*/
public static final Region TREE_CELL = new Region("TreeCell");
public static final Region TREE_CELL = new Region("TreeCell", true);
/**
* Viewport region. To bind a style to this <code>Region</code> use
* the name <code>Viewport</code>.
*/
public static final Region VIEWPORT = new Region("Viewport", "ViewportUI");
public static final Region VIEWPORT = new Region("Viewport", false);
private static Map<String, Region> getUItoRegionMap() {
AppContext context = AppContext.getAppContext();
Map<String, Region> map = (Map<String, Region>) context.get(UI_TO_REGION_MAP_KEY);
if (map == null) {
map = new HashMap<String, Region>();
map.put("ArrowButtonUI", ARROW_BUTTON);
map.put("ButtonUI", BUTTON);
map.put("CheckBoxUI", CHECK_BOX);
map.put("CheckBoxMenuItemUI", CHECK_BOX_MENU_ITEM);
map.put("ColorChooserUI", COLOR_CHOOSER);
map.put("ComboBoxUI", COMBO_BOX);
map.put("DesktopPaneUI", DESKTOP_PANE);
map.put("DesktopIconUI", DESKTOP_ICON);
map.put("EditorPaneUI", EDITOR_PANE);
map.put("FileChooserUI", FILE_CHOOSER);
map.put("FormattedTextFieldUI", FORMATTED_TEXT_FIELD);
map.put("InternalFrameUI", INTERNAL_FRAME);
map.put("InternalFrameTitlePaneUI", INTERNAL_FRAME_TITLE_PANE);
map.put("LabelUI", LABEL);
map.put("ListUI", LIST);
map.put("MenuUI", MENU);
map.put("MenuBarUI", MENU_BAR);
map.put("MenuItemUI", MENU_ITEM);
map.put("OptionPaneUI", OPTION_PANE);
map.put("PanelUI", PANEL);
map.put("PasswordFieldUI", PASSWORD_FIELD);
map.put("PopupMenuUI", POPUP_MENU);
map.put("PopupMenuSeparatorUI", POPUP_MENU_SEPARATOR);
map.put("ProgressBarUI", PROGRESS_BAR);
map.put("RadioButtonUI", RADIO_BUTTON);
map.put("RadioButtonMenuItemUI", RADIO_BUTTON_MENU_ITEM);
map.put("RootPaneUI", ROOT_PANE);
map.put("ScrollBarUI", SCROLL_BAR);
map.put("ScrollPaneUI", SCROLL_PANE);
map.put("SeparatorUI", SEPARATOR);
map.put("SliderUI", SLIDER);
map.put("SpinnerUI", SPINNER);
map.put("SplitPaneUI", SPLIT_PANE);
map.put("TabbedPaneUI", TABBED_PANE);
map.put("TableUI", TABLE);
map.put("TableHeaderUI", TABLE_HEADER);
map.put("TextAreaUI", TEXT_AREA);
map.put("TextFieldUI", TEXT_FIELD);
map.put("TextPaneUI", TEXT_PANE);
map.put("ToggleButtonUI", TOGGLE_BUTTON);
map.put("ToolBarUI", TOOL_BAR);
map.put("ToolTipUI", TOOL_TIP);
map.put("ToolBarSeparatorUI", TOOL_BAR_SEPARATOR);
map.put("TreeUI", TREE);
map.put("ViewportUI", VIEWPORT);
context.put(UI_TO_REGION_MAP_KEY, map);
}
return map;
}
private String name;
private boolean subregion;
private static Map<Region, String> getLowerCaseNameMap() {
AppContext context = AppContext.getAppContext();
Map<Region, String> map = (Map<Region, String>) context.get(LOWER_CASE_NAME_MAP_KEY);
if (map == null) {
map = new HashMap<Region, String>();
context.put(LOWER_CASE_NAME_MAP_KEY, map);
}
return map;
}
static Region getRegion(JComponent c) {
return uiToRegionMap.get(c.getUIClassID());
return getUItoRegionMap().get(c.getUIClassID());
}
static void registerUIs(UIDefaults table) {
for (String key : uiToRegionMap.keySet()) {
for (Object key : getUItoRegionMap().keySet()) {
table.put(key, "javax.swing.plaf.synth.SynthLookAndFeel");
}
}
private final String name;
private final boolean subregion;
Region(String name) {
this(name, null, true);
}
Region(String name, String ui) {
this(name, ui, false);
private Region(String name, boolean subregion) {
if (name == null) {
throw new NullPointerException("You must specify a non-null name");
}
this.name = name;
this.subregion = subregion;
}
/**
@ -481,14 +523,10 @@ public class Region {
* @param subregion Whether or not this is a subregion.
*/
protected Region(String name, String ui, boolean subregion) {
if (name == null) {
throw new NullPointerException("You must specify a non-null name");
}
this.name = name;
this(name, subregion);
if (ui != null) {
uiToRegionMap.put(ui, this);
getUItoRegionMap().put(ui, this);
}
this.subregion = subregion;
}
/**
@ -514,16 +552,17 @@ public class Region {
/**
* Returns the name, in lowercase.
*
* @return lower case representation of the name of the Region
*/
String getLowerCaseName() {
synchronized(lowerCaseNameMap) {
String lowerCaseName = lowerCaseNameMap.get(this);
if (lowerCaseName == null) {
lowerCaseName = getName().toLowerCase();
lowerCaseNameMap.put(this, lowerCaseName);
}
return lowerCaseName;
Map<Region, String> lowerCaseNameMap = getLowerCaseNameMap();
String lowerCaseName = lowerCaseNameMap.get(this);
if (lowerCaseName == null) {
lowerCaseName = name.toLowerCase(Locale.ENGLISH);
lowerCaseNameMap.put(this, lowerCaseName);
}
return lowerCaseName;
}
/**
@ -531,6 +570,7 @@ public class Region {
*
* @return name of the Region.
*/
@Override
public String toString() {
return name;
}

View File

@ -540,30 +540,7 @@ public class GlyphView extends View implements TabableView, Cloneable {
*/
@Override
public float getMinimumSpan(int axis) {
switch (axis) {
case View.X_AXIS:
if (minimumSpan < 0) {
minimumSpan = 0;
int p0 = getStartOffset();
int p1 = getEndOffset();
while (p1 > p0) {
int breakSpot = getBreakSpot(p0, p1);
if (breakSpot == BreakIterator.DONE) {
// the rest of the view is non-breakable
breakSpot = p0;
}
minimumSpan = Math.max(minimumSpan,
getPartialSpan(breakSpot, p1));
// Note: getBreakSpot returns the *last* breakspot
p1 = breakSpot - 1;
}
}
return minimumSpan;
case View.Y_AXIS:
return super.getMinimumSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
return super.getMinimumSpan(axis);
}
/**

View File

@ -25,6 +25,7 @@
package javax.swing.text;
import java.util.Vector;
import sun.awt.AppContext;
/**
* A queue of text layout tasks.
@ -35,10 +36,10 @@ import java.util.Vector;
*/
public class LayoutQueue {
Vector<Runnable> tasks;
Thread worker;
private static final Object DEFAULT_QUEUE = new Object();
static LayoutQueue defaultQueue;
private Vector<Runnable> tasks;
private Thread worker;
/**
* Construct a layout queue.
@ -51,10 +52,15 @@ public class LayoutQueue {
* Fetch the default layout queue.
*/
public static LayoutQueue getDefaultQueue() {
if (defaultQueue == null) {
defaultQueue = new LayoutQueue();
AppContext ac = AppContext.getAppContext();
synchronized (DEFAULT_QUEUE) {
LayoutQueue defaultQueue = (LayoutQueue) ac.get(DEFAULT_QUEUE);
if (defaultQueue == null) {
defaultQueue = new LayoutQueue();
ac.put(DEFAULT_QUEUE, defaultQueue);
}
return defaultQueue;
}
return defaultQueue;
}
/**
@ -63,7 +69,9 @@ public class LayoutQueue {
* @param q the new queue.
*/
public static void setDefaultQueue(LayoutQueue q) {
defaultQueue = q;
synchronized (DEFAULT_QUEUE) {
AppContext.getAppContext().put(DEFAULT_QUEUE, q);
}
}
/**

View File

@ -721,35 +721,7 @@ public class ParagraphView extends FlowView implements TabExpander {
@Override
protected SizeRequirements calculateMinorAxisRequirements(int axis,
SizeRequirements r) {
r = super.calculateMinorAxisRequirements(axis, r);
float min = 0;
float glue = 0;
int n = getLayoutViewCount();
for (int i = 0; i < n; i++) {
View v = getLayoutView(i);
float span = v.getMinimumSpan(axis);
if (v.getBreakWeight(axis, 0, v.getMaximumSpan(axis))
> View.BadBreakWeight) {
// find the longest non-breakable fragments at the view edges
int p0 = v.getStartOffset();
int p1 = v.getEndOffset();
float start = findEdgeSpan(v, axis, p0, p0, p1);
float end = findEdgeSpan(v, axis, p1, p0, p1);
glue += start;
min = Math.max(min, Math.max(span, glue));
glue = end;
} else {
// non-breakable view
glue += span;
min = Math.max(min, glue);
}
}
r.minimum = Math.max(r.minimum, (int) min);
r.preferred = Math.max(r.minimum, r.preferred);
r.maximum = Math.max(r.preferred, r.maximum);
return r;
return super.calculateMinorAxisRequirements(axis, r);
}
/**

View File

@ -327,13 +327,45 @@ public class WrappedPlainView extends BoxView implements TabExpander {
/**
* Return reasonable default values for the view dimensions. The standard
* text terminal size 80x24 is pretty suitable for the wrapped plain view.
*
* The size should not be larger than the component housing the view's
* container.
*/
private float getDefaultSpan(int axis) {
Container host = getContainer();
Component parent = null;
if (host != null) {
parent = host.getParent();
}
switch (axis) {
case View.X_AXIS:
return 80 * metrics.getWidths()['M'];
int defaultWidth = 80 * metrics.getWidths()['M'];
int parentWidth = 0;
if (parent != null) {
parentWidth = parent.getWidth();
}
if (defaultWidth > parentWidth) {
return parentWidth;
}
return defaultWidth;
case View.Y_AXIS:
return 24 * metrics.getHeight();
int defaultHeight = 24 * metrics.getHeight();
int parentHeight = 0;
if (parent != null) {
parentHeight = parent.getHeight();
}
if (defaultHeight > parentHeight) {
return parentHeight;
}
return defaultHeight;
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}

View File

@ -19,7 +19,7 @@
*
*/
/*
* Copyright 2005-2008 Sun Microsystems, Inc. All rights reserved.
* Copyright 2005-2009 Sun Microsystems, Inc. All rights reserved.
*/
/*
* $Id: DOMHMACSignatureMethod.java,v 1.2 2008/07/24 15:20:32 mullan Exp $
@ -58,6 +58,7 @@ public abstract class DOMHMACSignatureMethod extends DOMSignatureMethod {
Logger.getLogger("org.jcp.xml.dsig.internal.dom");
private Mac hmac;
private int outputLength;
private boolean outputLengthSet;
/**
* Creates a <code>DOMHMACSignatureMethod</code> with the specified params
@ -87,6 +88,7 @@ public abstract class DOMHMACSignatureMethod extends DOMSignatureMethod {
("params must be of type HMACParameterSpec");
}
outputLength = ((HMACParameterSpec) params).getOutputLength();
outputLengthSet = true;
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE,
"Setting outputLength from HMACParameterSpec to: "
@ -101,6 +103,7 @@ public abstract class DOMHMACSignatureMethod extends DOMSignatureMethod {
throws MarshalException {
outputLength = new Integer
(paramsElem.getFirstChild().getNodeValue()).intValue();
outputLengthSet = true;
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "unmarshalled outputLength: " + outputLength);
}
@ -135,23 +138,13 @@ public abstract class DOMHMACSignatureMethod extends DOMSignatureMethod {
throw new XMLSignatureException(nsae);
}
}
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "outputLength = " + outputLength);
if (outputLengthSet && outputLength < getDigestLength()) {
throw new XMLSignatureException
("HMACOutputLength must not be less than " + getDigestLength());
}
hmac.init((SecretKey) key);
si.canonicalize(context, new MacOutputStream(hmac));
byte[] result = hmac.doFinal();
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "resultLength = " + result.length);
}
if (outputLength != -1) {
int byteLength = outputLength/8;
if (result.length > byteLength) {
byte[] truncated = new byte[byteLength];
System.arraycopy(result, 0, truncated, 0, byteLength);
result = truncated;
}
}
return MessageDigest.isEqual(sig, result);
}
@ -171,18 +164,13 @@ public abstract class DOMHMACSignatureMethod extends DOMSignatureMethod {
throw new XMLSignatureException(nsae);
}
}
if (outputLengthSet && outputLength < getDigestLength()) {
throw new XMLSignatureException
("HMACOutputLength must not be less than " + getDigestLength());
}
hmac.init((SecretKey) key);
si.canonicalize(context, new MacOutputStream(hmac));
byte[] result = hmac.doFinal();
if (outputLength != -1) {
int byteLength = outputLength/8;
if (result.length > byteLength) {
byte[] truncated = new byte[byteLength];
System.arraycopy(result, 0, truncated, 0, byteLength);
result = truncated;
}
}
return result;
return hmac.doFinal();
}
boolean paramsEqual(AlgorithmParameterSpec spec) {
@ -197,6 +185,11 @@ public abstract class DOMHMACSignatureMethod extends DOMSignatureMethod {
return (outputLength == ospec.getOutputLength());
}
/**
* Returns the output length of the hash/digest.
*/
abstract int getDigestLength();
static final class SHA1 extends DOMHMACSignatureMethod {
SHA1(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
@ -211,6 +204,9 @@ public abstract class DOMHMACSignatureMethod extends DOMSignatureMethod {
String getSignatureAlgorithm() {
return "HmacSHA1";
}
int getDigestLength() {
return 160;
}
}
static final class SHA256 extends DOMHMACSignatureMethod {
@ -227,6 +223,9 @@ public abstract class DOMHMACSignatureMethod extends DOMSignatureMethod {
String getSignatureAlgorithm() {
return "HmacSHA256";
}
int getDigestLength() {
return 256;
}
}
static final class SHA384 extends DOMHMACSignatureMethod {
@ -243,6 +242,9 @@ public abstract class DOMHMACSignatureMethod extends DOMSignatureMethod {
String getSignatureAlgorithm() {
return "HmacSHA384";
}
int getDigestLength() {
return 384;
}
}
static final class SHA512 extends DOMHMACSignatureMethod {
@ -259,5 +261,8 @@ public abstract class DOMHMACSignatureMethod extends DOMSignatureMethod {
String getSignatureAlgorithm() {
return "HmacSHA512";
}
int getDigestLength() {
return 512;
}
}
}

View File

@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Sf, tifth Floor, Boston, MA 02110-1301 USA.
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or

View File

@ -51,6 +51,7 @@ import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.cert.Certificate;
import sun.misc.FileURLMapper;
import sun.net.util.URLUtil;
/**
* This class is used to maintain a search path of URLs for loading classes
@ -80,7 +81,7 @@ public class URLClassPath {
ArrayList<Loader> loaders = new ArrayList<Loader>();
/* Map of each URL opened to its corresponding Loader */
HashMap<URL, Loader> lmap = new HashMap<URL, Loader>();
HashMap<String, Loader> lmap = new HashMap<String, Loader>();
/* The jar protocol handler to use when creating new URLs */
private URLStreamHandler jarHandler;
@ -317,7 +318,8 @@ public class URLClassPath {
// Skip this URL if it already has a Loader. (Loader
// may be null in the case where URL has not been opened
// but is referenced by a JAR index.)
if (lmap.containsKey(url)) {
String urlNoFragString = URLUtil.urlNoFragString(url);
if (lmap.containsKey(urlNoFragString)) {
continue;
}
// Otherwise, create a new Loader for the URL.
@ -336,7 +338,7 @@ public class URLClassPath {
}
// Finally, add the Loader to the search path.
loaders.add(loader);
lmap.put(url, loader);
lmap.put(urlNoFragString, loader);
}
return loaders.get(index);
}
@ -576,7 +578,7 @@ public class URLClassPath {
private JarIndex index;
private MetaIndex metaIndex;
private URLStreamHandler handler;
private HashMap<URL, Loader> lmap;
private HashMap<String, Loader> lmap;
private boolean closed = false;
/*
@ -584,7 +586,7 @@ public class URLClassPath {
* a JAR file.
*/
JarLoader(URL url, URLStreamHandler jarHandler,
HashMap<URL, Loader> loaderMap)
HashMap<String, Loader> loaderMap)
throws IOException
{
super(new URL("jar", "", -1, url + "!/", jarHandler));
@ -663,8 +665,9 @@ public class URLClassPath {
try {
URL jarURL = new URL(csu, jarfiles[i]);
// If a non-null loader already exists, leave it alone.
if (!lmap.containsKey(jarURL)) {
lmap.put(jarURL, null);
String urlNoFragString = URLUtil.urlNoFragString(jarURL);
if (!lmap.containsKey(urlNoFragString)) {
lmap.put(urlNoFragString, null);
}
} catch (MalformedURLException e) {
continue;
@ -806,7 +809,7 @@ public class URLClassPath {
if (index == null)
return null;
HashSet<URL> visited = new HashSet<URL>();
HashSet<String> visited = new HashSet<String>();
return getResource(name, check, visited);
}
@ -818,7 +821,7 @@ public class URLClassPath {
* non-existent resource
*/
Resource getResource(final String name, boolean check,
Set<URL> visited) {
Set<String> visited) {
Resource res;
Object[] jarFiles;
@ -843,7 +846,8 @@ public class URLClassPath {
try{
url = new URL(csu, jarName);
if ((newLoader = (JarLoader)lmap.get(url)) == null) {
String urlNoFragString = URLUtil.urlNoFragString(url);
if ((newLoader = (JarLoader)lmap.get(urlNoFragString)) == null) {
/* no loader has been set up for this jar file
* before
*/
@ -867,7 +871,7 @@ public class URLClassPath {
}
/* put it in the global hashtable */
lmap.put(url, newLoader);
lmap.put(urlNoFragString, newLoader);
}
} catch (java.security.PrivilegedActionException pae) {
continue;
@ -879,7 +883,7 @@ public class URLClassPath {
/* Note that the addition of the url to the list of visited
* jars incorporates a check for presence in the hashmap
*/
boolean visitedURL = !visited.add(url);
boolean visitedURL = !visited.add(URLUtil.urlNoFragString(url));
if (!visitedURL) {
try {
newLoader.ensureOpen();

View File

@ -0,0 +1,43 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.net;
import java.net.Proxy;
import java.net.SocketAddress;
/**
* Proxy wrapper class so that we can determine application set
* proxies by type.
*/
public final class ApplicationProxy extends Proxy {
private ApplicationProxy(Proxy proxy) {
super(proxy.type(), proxy.address());
}
public static ApplicationProxy create(Proxy proxy) {
return new ApplicationProxy(proxy);
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.net.util;
import java.net.URL;
/**
* URL Utility class.
*/
public class URLUtil {
/**
* Returns a string form of the url suitable for use as a key in HashMap/Sets.
*
* The string form should be behave in the same manner as the URL when
* compared for equality in a HashMap/Set, except that no nameservice
* lookup is done on the hostname (only string comparison), and the fragment
* is not considered.
*
* @see java.net.URLStreamHandler.sameFile(java.net.URL)
*/
public static String urlNoFragString(URL url) {
StringBuilder strForm = new StringBuilder();
String protocol = url.getProtocol();
if (protocol != null) {
/* protocol is compared case-insensitive, so convert to lowercase */
protocol = protocol.toLowerCase();
strForm.append(protocol);
strForm.append("://");
}
String host = url.getHost();
if (host != null) {
/* host is compared case-insensitive, so convert to lowercase */
host = host.toLowerCase();
strForm.append(host);
int port = url.getPort();
if (port == -1) {
/* if no port is specificed then use the protocols
* default, if there is one */
port = url.getDefaultPort();
}
if (port != -1) {
strForm.append(":").append(port);
}
}
String file = url.getFile();
if (file != null) {
strForm.append(file);
}
return strForm.toString();
}
}

View File

@ -578,12 +578,20 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
responses = new MessageHeader();
this.handler = handler;
instProxy = p;
cookieHandler = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<CookieHandler>() {
if (instProxy instanceof sun.net.ApplicationProxy) {
/* Application set Proxies should not have access to cookies
* in a secure environment unless explicitly allowed. */
try {
cookieHandler = CookieHandler.getDefault();
} catch (SecurityException se) { /* swallow exception */ }
} else {
cookieHandler = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<CookieHandler>() {
public CookieHandler run() {
return CookieHandler.getDefault();
}
});
return CookieHandler.getDefault();
}
});
}
cacheHandler = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<ResponseCache>() {
public ResponseCache run() {

View File

@ -0,0 +1,189 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.security.ec;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
* KeyAgreement implementation for ECDH.
*
* @since 1.7
*/
public final class ECDHKeyAgreement extends KeyAgreementSpi {
// flag indicating whether the native ECC implementation is present
private static boolean implementationPresent = true;
static {
try {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("sunecc");
return null;
}
});
} catch (UnsatisfiedLinkError e) {
implementationPresent = false;
}
}
// private key, if initialized
private ECPrivateKey privateKey;
// encoded public point, non-null between doPhase() & generateSecret() only
private byte[] publicValue;
// length of the secret to be derived
private int secretLen;
/**
* Constructs a new ECDHKeyAgreement.
*
* @exception ProviderException if the native ECC library is unavailable.
*/
public ECDHKeyAgreement() {
if (!implementationPresent) {
throw new ProviderException("ECDH implementation is not available");
}
}
// see JCE spec
protected void engineInit(Key key, SecureRandom random)
throws InvalidKeyException {
if (!(key instanceof PrivateKey)) {
throw new InvalidKeyException
("Key must be instance of PrivateKey");
}
privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key);
publicValue = null;
}
// see JCE spec
protected void engineInit(Key key, AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
}
engineInit(key, random);
}
// see JCE spec
protected Key engineDoPhase(Key key, boolean lastPhase)
throws InvalidKeyException, IllegalStateException {
if (privateKey == null) {
throw new IllegalStateException("Not initialized");
}
if (publicValue != null) {
throw new IllegalStateException("Phase already executed");
}
if (!lastPhase) {
throw new IllegalStateException
("Only two party agreement supported, lastPhase must be true");
}
if (!(key instanceof ECPublicKey)) {
throw new InvalidKeyException
("Key must be a PublicKey with algorithm EC");
}
ECPublicKey ecKey = (ECPublicKey)key;
ECParameterSpec params = ecKey.getParams();
if (ecKey instanceof ECPublicKeyImpl) {
publicValue = ((ECPublicKeyImpl)ecKey).getEncodedPublicValue();
} else { // instanceof ECPublicKey
publicValue =
ECParameters.encodePoint(ecKey.getW(), params.getCurve());
}
int keyLenBits = params.getCurve().getField().getFieldSize();
secretLen = (keyLenBits + 7) >> 3;
return null;
}
// see JCE spec
protected byte[] engineGenerateSecret() throws IllegalStateException {
if ((privateKey == null) || (publicValue == null)) {
throw new IllegalStateException("Not initialized correctly");
}
byte[] s = privateKey.getS().toByteArray();
byte[] encodedParams =
ECParameters.encodeParameters(privateKey.getParams()); // DER OID
try {
return deriveKey(s, publicValue, encodedParams);
} catch (GeneralSecurityException e) {
throw new ProviderException("Could not derive key", e);
}
}
// see JCE spec
protected int engineGenerateSecret(byte[] sharedSecret, int
offset) throws IllegalStateException, ShortBufferException {
if (offset + secretLen > sharedSecret.length) {
throw new ShortBufferException("Need " + secretLen
+ " bytes, only " + (sharedSecret.length - offset) + " available");
}
byte[] secret = engineGenerateSecret();
System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
return secret.length;
}
// see JCE spec
protected SecretKey engineGenerateSecret(String algorithm)
throws IllegalStateException, NoSuchAlgorithmException,
InvalidKeyException {
if (algorithm == null) {
throw new NoSuchAlgorithmException("Algorithm must not be null");
}
if (!(algorithm.equals("TlsPremasterSecret"))) {
throw new NoSuchAlgorithmException
("Only supported for algorithm TlsPremasterSecret");
}
return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret");
}
/**
* Generates a secret key using the public and private keys.
*
* @param s the private key's S value.
* @param w the public key's W point (in uncompressed form).
* @param encodedParams the curve's DER encoded object identifier.
*
* @return byte[] the secret key.
*/
private static native byte[] deriveKey(byte[] s, byte[] w,
byte[] encodedParams) throws GeneralSecurityException;
}

View File

@ -0,0 +1,447 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.security.ec;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.math.BigInteger;
import java.util.Arrays;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import sun.security.jca.JCAUtil;
import sun.security.util.*;
import sun.security.x509.AlgorithmId;
/**
* ECDSA signature implementation. This class currently supports the
* following algorithm names:
*
* . "NONEwithECDSA"
* . "SHA1withECDSA"
* . "SHA256withECDSA"
* . "SHA384withECDSA"
* . "SHA512withECDSA"
*
* @since 1.7
*/
abstract class ECDSASignature extends SignatureSpi {
// flag indicating whether the native ECC implementation is present
private static boolean implementationPresent = true;
static {
try {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("sunecc");
return null;
}
});
} catch (UnsatisfiedLinkError e) {
implementationPresent = false;
}
}
// message digest implementation we use
private final MessageDigest messageDigest;
// supplied entropy
private SecureRandom random;
// flag indicating whether the digest has been reset
private boolean needsReset;
// private key, if initialized for signing
private ECPrivateKey privateKey;
// public key, if initialized for verifying
private ECPublicKey publicKey;
/**
* Constructs a new ECDSASignature. Used by Raw subclass.
*
* @exception ProviderException if the native ECC library is unavailable.
*/
ECDSASignature() {
if (!implementationPresent) {
throw new
ProviderException("ECDSA implementation is not available");
}
messageDigest = null;
}
/**
* Constructs a new ECDSASignature. Used by subclasses.
*
* @exception ProviderException if the native ECC library is unavailable.
*/
ECDSASignature(String digestName) {
if (!implementationPresent) {
throw new
ProviderException("ECDSA implementation is not available");
}
try {
messageDigest = MessageDigest.getInstance(digestName);
} catch (NoSuchAlgorithmException e) {
throw new ProviderException(e);
}
needsReset = false;
}
// Nested class for NONEwithECDSA signatures
public static final class Raw extends ECDSASignature {
// the longest supported digest is 512 bits (SHA-512)
private static final int RAW_ECDSA_MAX = 64;
private final byte[] precomputedDigest;
private int offset = 0;
public Raw() {
precomputedDigest = new byte[RAW_ECDSA_MAX];
}
// Stores the precomputed message digest value.
@Override
protected void engineUpdate(byte b) throws SignatureException {
if (offset >= precomputedDigest.length) {
offset = RAW_ECDSA_MAX + 1;
return;
}
precomputedDigest[offset++] = b;
}
// Stores the precomputed message digest value.
@Override
protected void engineUpdate(byte[] b, int off, int len)
throws SignatureException {
if (offset >= precomputedDigest.length) {
offset = RAW_ECDSA_MAX + 1;
return;
}
System.arraycopy(b, off, precomputedDigest, offset, len);
offset += len;
}
// Stores the precomputed message digest value.
@Override
protected void engineUpdate(ByteBuffer byteBuffer) {
int len = byteBuffer.remaining();
if (len <= 0) {
return;
}
if (offset + len >= precomputedDigest.length) {
offset = RAW_ECDSA_MAX + 1;
return;
}
byteBuffer.get(precomputedDigest, offset, len);
offset += len;
}
@Override
protected void resetDigest(){
offset = 0;
}
// Returns the precomputed message digest value.
@Override
protected byte[] getDigestValue() throws SignatureException {
if (offset > RAW_ECDSA_MAX) {
throw new SignatureException("Message digest is too long");
}
byte[] result = new byte[offset];
System.arraycopy(precomputedDigest, 0, result, 0, offset);
offset = 0;
return result;
}
}
// Nested class for SHA1withECDSA signatures
public static final class SHA1 extends ECDSASignature {
public SHA1() {
super("SHA1");
}
}
// Nested class for SHA256withECDSA signatures
public static final class SHA256 extends ECDSASignature {
public SHA256() {
super("SHA-256");
}
}
// Nested class for SHA384withECDSA signatures
public static final class SHA384 extends ECDSASignature {
public SHA384() {
super("SHA-384");
}
}
// Nested class for SHA512withECDSA signatures
public static final class SHA512 extends ECDSASignature {
public SHA512() {
super("SHA-512");
}
}
// initialize for verification. See JCA doc
@Override
protected void engineInitVerify(PublicKey publicKey)
throws InvalidKeyException {
this.publicKey = (ECPublicKey) ECKeyFactory.toECKey(publicKey);
// Should check that the supplied key is appropriate for signature
// algorithm (e.g. P-256 for SHA256withECDSA)
this.privateKey = null;
resetDigest();
}
// initialize for signing. See JCA doc
@Override
protected void engineInitSign(PrivateKey privateKey)
throws InvalidKeyException {
engineInitSign(privateKey, null);
}
// initialize for signing. See JCA doc
@Override
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
throws InvalidKeyException {
this.privateKey = (ECPrivateKey) ECKeyFactory.toECKey(privateKey);
// Should check that the supplied key is appropriate for signature
// algorithm (e.g. P-256 for SHA256withECDSA)
this.publicKey = null;
this.random = random;
resetDigest();
}
/**
* Resets the message digest if needed.
*/
protected void resetDigest() {
if (needsReset) {
if (messageDigest != null) {
messageDigest.reset();
}
needsReset = false;
}
}
/**
* Returns the message digest value.
*/
protected byte[] getDigestValue() throws SignatureException {
needsReset = false;
return messageDigest.digest();
}
// update the signature with the plaintext data. See JCA doc
@Override
protected void engineUpdate(byte b) throws SignatureException {
messageDigest.update(b);
needsReset = true;
}
// update the signature with the plaintext data. See JCA doc
@Override
protected void engineUpdate(byte[] b, int off, int len)
throws SignatureException {
messageDigest.update(b, off, len);
needsReset = true;
}
// update the signature with the plaintext data. See JCA doc
@Override
protected void engineUpdate(ByteBuffer byteBuffer) {
int len = byteBuffer.remaining();
if (len <= 0) {
return;
}
messageDigest.update(byteBuffer);
needsReset = true;
}
// sign the data and return the signature. See JCA doc
@Override
protected byte[] engineSign() throws SignatureException {
byte[] s = privateKey.getS().toByteArray();
ECParameterSpec params = privateKey.getParams();
byte[] encodedParams = ECParameters.encodeParameters(params); // DER OID
int keySize = params.getCurve().getField().getFieldSize();
// seed is twice the key size (in bytes)
byte[] seed = new byte[((keySize + 7) >> 3) * 2];
if (random == null) {
random = JCAUtil.getSecureRandom();
}
random.nextBytes(seed);
try {
return encodeSignature(
signDigest(getDigestValue(), s, encodedParams, seed));
} catch (GeneralSecurityException e) {
throw new SignatureException("Could not sign data", e);
}
}
// verify the data and return the result. See JCA doc
@Override
protected boolean engineVerify(byte[] signature) throws SignatureException {
byte[] w;
ECParameterSpec params = publicKey.getParams();
byte[] encodedParams = ECParameters.encodeParameters(params); // DER OID
if (publicKey instanceof ECPublicKeyImpl) {
w = ((ECPublicKeyImpl)publicKey).getEncodedPublicValue();
} else { // instanceof ECPublicKey
w = ECParameters.encodePoint(publicKey.getW(), params.getCurve());
}
try {
return verifySignedDigest(
decodeSignature(signature), getDigestValue(), w, encodedParams);
} catch (GeneralSecurityException e) {
throw new SignatureException("Could not verify signature", e);
}
}
// set parameter, not supported. See JCA doc
@Override
protected void engineSetParameter(String param, Object value)
throws InvalidParameterException {
throw new UnsupportedOperationException("setParameter() not supported");
}
// get parameter, not supported. See JCA doc
@Override
protected Object engineGetParameter(String param)
throws InvalidParameterException {
throw new UnsupportedOperationException("getParameter() not supported");
}
// Convert the concatenation of R and S into their DER encoding
private byte[] encodeSignature(byte[] signature) throws SignatureException {
try {
int n = signature.length >> 1;
byte[] bytes = new byte[n];
System.arraycopy(signature, 0, bytes, 0, n);
BigInteger r = new BigInteger(1, bytes);
System.arraycopy(signature, n, bytes, 0, n);
BigInteger s = new BigInteger(1, bytes);
DerOutputStream out = new DerOutputStream(signature.length + 10);
out.putInteger(r);
out.putInteger(s);
DerValue result =
new DerValue(DerValue.tag_Sequence, out.toByteArray());
return result.toByteArray();
} catch (Exception e) {
throw new SignatureException("Could not encode signature", e);
}
}
// Convert the DER encoding of R and S into a concatenation of R and S
private byte[] decodeSignature(byte[] signature) throws SignatureException {
try {
DerInputStream in = new DerInputStream(signature);
DerValue[] values = in.getSequence(2);
BigInteger r = values[0].getPositiveBigInteger();
BigInteger s = values[1].getPositiveBigInteger();
// trim leading zeroes
byte[] rBytes = trimZeroes(r.toByteArray());
byte[] sBytes = trimZeroes(s.toByteArray());
int k = Math.max(rBytes.length, sBytes.length);
// r and s each occupy half the array
byte[] result = new byte[k << 1];
System.arraycopy(rBytes, 0, result, k - rBytes.length,
rBytes.length);
System.arraycopy(sBytes, 0, result, result.length - sBytes.length,
sBytes.length);
return result;
} catch (Exception e) {
throw new SignatureException("Could not decode signature", e);
}
}
// trim leading (most significant) zeroes from the result
private static byte[] trimZeroes(byte[] b) {
int i = 0;
while ((i < b.length - 1) && (b[i] == 0)) {
i++;
}
if (i == 0) {
return b;
}
byte[] t = new byte[b.length - i];
System.arraycopy(b, i, t, 0, t.length);
return t;
}
/**
* Signs the digest using the private key.
*
* @param digest the digest to be signed.
* @param s the private key's S value.
* @param encodedParams the curve's DER encoded object identifier.
* @param seed the random seed.
*
* @return byte[] the signature.
*/
private static native byte[] signDigest(byte[] digest, byte[] s,
byte[] encodedParams, byte[] seed) throws GeneralSecurityException;
/**
* Verifies the signed digest using the public key.
*
* @param signedDigest the signature to be verified. It is encoded
* as a concatenation of the key's R and S values.
* @param digest the digest to be used.
* @param w the public key's W point (in uncompressed form).
* @param encodedParams the curve's DER encoded object identifier.
*
* @return boolean true if the signature is successfully verified.
*/
private static native boolean verifySignedDigest(byte[] signature,
byte[] digest, byte[] w, byte[] encodedParams)
throws GeneralSecurityException;
}

View File

@ -0,0 +1,191 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.security.ec;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import sun.security.ec.NamedCurve;
import sun.security.ec.ECParameters;
import sun.security.ec.ECPrivateKeyImpl;
import sun.security.ec.ECPublicKeyImpl;
import sun.security.jca.JCAUtil;
/**
* EC keypair generator.
* Standard algorithm, minimum key length is 112 bits, maximum is 571 bits.
*
* @since 1.7
*/
public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
// flag indicating whether the native ECC implementation is present
private static boolean implementationPresent = true;
static {
try {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("sunecc");
return null;
}
});
} catch (UnsatisfiedLinkError e) {
implementationPresent = false;
}
}
private static final int KEY_SIZE_MIN = 112; // min bits (see ecc_impl.h)
private static final int KEY_SIZE_MAX = 571; // max bits (see ecc_impl.h)
private static final int KEY_SIZE_DEFAULT = 256;
// used to seed the keypair generator
private SecureRandom random;
// size of the key to generate, KEY_SIZE_MIN <= keySize <= KEY_SIZE_MAX
private int keySize;
// parameters specified via init, if any
private AlgorithmParameterSpec params = null;
/**
* Constructs a new ECKeyPairGenerator.
*
* @exception ProviderException if the native ECC library is unavailable.
*/
public ECKeyPairGenerator() {
if (!implementationPresent) {
throw new ProviderException("EC implementation is not available");
}
// initialize to default in case the app does not call initialize()
initialize(KEY_SIZE_DEFAULT, null);
}
// initialize the generator. See JCA doc
@Override
public void initialize(int keySize, SecureRandom random) {
checkKeySize(keySize);
this.params = NamedCurve.getECParameterSpec(keySize);
if (params == null) {
throw new InvalidParameterException(
"No EC parameters available for key size " + keySize + " bits");
}
this.random = random;
}
// second initialize method. See JCA doc
@Override
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
throws InvalidAlgorithmParameterException {
if (params instanceof ECParameterSpec) {
this.params = ECParameters.getNamedCurve((ECParameterSpec)params);
if (this.params == null) {
throw new InvalidAlgorithmParameterException(
"Unsupported curve: " + params);
}
} else if (params instanceof ECGenParameterSpec) {
String name = ((ECGenParameterSpec)params).getName();
this.params = NamedCurve.getECParameterSpec(name);
if (this.params == null) {
throw new InvalidAlgorithmParameterException(
"Unknown curve name: " + name);
}
} else {
throw new InvalidAlgorithmParameterException(
"ECParameterSpec or ECGenParameterSpec required for EC");
}
this.keySize =
((ECParameterSpec)this.params).getCurve().getField().getFieldSize();
this.random = random;
}
// generate the keypair. See JCA doc
@Override
public KeyPair generateKeyPair() {
byte[] encodedParams =
ECParameters.encodeParameters((ECParameterSpec)params);
// seed is twice the key size (in bytes)
byte[] seed = new byte[2 * ((keySize + 7) >> 3)];
if (random == null) {
random = JCAUtil.getSecureRandom();
}
random.nextBytes(seed);
long[] handles = generateECKeyPair(keySize, encodedParams, seed);
// The 'params' object supplied above is equivalent to the native one
// so there is no need to fetch it.
// handles[0] points to the native private key
BigInteger s = new BigInteger(1, getEncodedBytes(handles[0]));
try {
PrivateKey privateKey =
new ECPrivateKeyImpl(s, (ECParameterSpec)params);
// handles[1] points to the native public key
ECPoint w = ECParameters.decodePoint(getEncodedBytes(handles[1]),
((ECParameterSpec)params).getCurve());
PublicKey publicKey =
new ECPublicKeyImpl(w, (ECParameterSpec)params);
return new KeyPair(publicKey, privateKey);
} catch (Exception e) {
throw new ProviderException(e);
}
}
private void checkKeySize(int keySize) throws InvalidParameterException {
if (keySize < KEY_SIZE_MIN) {
throw new InvalidParameterException
("Key size must be at least " + KEY_SIZE_MIN + " bits");
}
if (keySize > KEY_SIZE_MAX) {
throw new InvalidParameterException
("Key size must be at most " + KEY_SIZE_MAX + " bits");
}
this.keySize = keySize;
}
/*
* Generates the keypair and returns a 2-element array of handles.
* The first handle points to the private key, the second to the public key.
*/
private static native long[] generateECKeyPair(int keySize,
byte[] encodedParams, byte[] seed);
/*
* Extracts the encoded key data using the supplied handle.
*/
private static native byte[] getEncodedBytes(long handle);
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.security.ec;
import java.util.*;
import java.security.*;
import sun.security.action.PutAllAction;
/**
* Provider class for the Elliptic Curve provider.
* Supports EC keypair and parameter generation, ECDSA signing and
* ECDH key agreement.
*
* IMPLEMENTATION NOTE:
* The Java classes in this provider access a native ECC implementation
* via JNI to a C++ wrapper class which in turn calls C functions.
* The Java classes are packaged into the signed sunec.jar in the JRE
* extensions directory and the C++ and C functions are packaged into
* libsunecc.so or sunecc.dll in the JRE native libraries directory.
*
* @since 1.7
*/
public final class SunEC extends Provider {
private static final long serialVersionUID = -2279741672933606418L;
public SunEC() {
super("SunEC", 1.7d, "Sun Elliptic Curve provider (EC, ECDSA, ECDH)");
// if there is no security manager installed, put directly into
// the provider. Otherwise, create a temporary map and use a
// doPrivileged() call at the end to transfer the contents
if (System.getSecurityManager() == null) {
SunECEntries.putEntries(this);
} else {
Map<Object, Object> map = new HashMap<Object, Object>();
SunECEntries.putEntries(map);
AccessController.doPrivileged(new PutAllAction(this, map));
}
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.security.ec;
import java.util.Map;
/**
* Defines the entries of the SunEC provider.
*
* @since 1.7
*/
final class SunECEntries {
private SunECEntries() {
// empty
}
static void putEntries(Map<Object, Object> map) {
/*
* Signature engines
*/
map.put("Signature.NONEwithECDSA",
"sun.security.ec.ECDSASignature$Raw");
map.put("Signature.SHA1withECDSA",
"sun.security.ec.ECDSASignature$SHA1");
map.put("Signature.SHA256withECDSA",
"sun.security.ec.ECDSASignature$SHA256");
map.put("Signature.SHA384withECDSA",
"sun.security.ec.ECDSASignature$SHA384");
map.put("Signature.SHA512withECDSA",
"sun.security.ec.ECDSASignature$SHA512");
String ecKeyClasses = "java.security.interfaces.ECPublicKey" +
"|java.security.interfaces.ECPrivateKey";
map.put("Signature.NONEwithECDSA SupportedKeyClasses", ecKeyClasses);
map.put("Signature.SHA1withECDSA SupportedKeyClasses", ecKeyClasses);
map.put("Signature.SHA256withECDSA SupportedKeyClasses", ecKeyClasses);
map.put("Signature.SHA384withECDSA SupportedKeyClasses", ecKeyClasses);
map.put("Signature.SHA512withECDSA SupportedKeyClasses", ecKeyClasses);
/*
* Key Pair Generator engine
*/
map.put("KeyPairGenerator.EC", "sun.security.ec.ECKeyPairGenerator");
map.put("Alg.Alias.KeyPairGenerator.EllipticCurve", "EC");
/*
* Key Factory engine
*/
map.put("KeyFactory.EC", "sun.security.ec.ECKeyFactory");
map.put("Alg.Alias.KeyFactory.EllipticCurve", "EC");
/*
* Algorithm Parameter engine
*/
map.put("AlgorithmParameters.EC", "sun.security.ec.ECParameters");
map.put("Alg.Alias.AlgorithmParameters.EllipticCurve", "EC");
/*
* Key Agreement engine
*/
map.put("KeyAgreement.ECDH", "sun.security.ec.ECDHKeyAgreement");
map.put("KeyAgreement.ECDH SupportedKeyClasses", ecKeyClasses);
/*
* Key sizes
*/
map.put("Signature.SHA1withECDSA KeySize", "256");
map.put("KeyPairGenerator.EC KeySize", "256");
map.put("AlgorithmParameterGenerator.ECDSA KeySize", "256");
/*
* Implementation type: software or hardware
*/
map.put("Signature.NONEwithECDSA ImplementedIn", "Software");
map.put("Signature.SHA1withECDSA ImplementedIn", "Software");
map.put("Signature.SHA256withECDSA ImplementedIn", "Software");
map.put("Signature.SHA384withECDSA ImplementedIn", "Software");
map.put("Signature.SHA512withECDSA ImplementedIn", "Software");
map.put("KeyPairGenerator.EC ImplementedIn", "Software");
map.put("KeyFactory.EC ImplementedIn", "Software");
map.put("KeyAgreement.ECDH ImplementedIn", "Software");
map.put("AlgorithmParameters.EC ImplementedIn", "Software");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,14 +27,13 @@ package sun.security.jgss;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.jgss.*;
import sun.security.util.ObjectIdentifier;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import com.sun.security.jgss.*;
/**
* This class represents the JGSS security context and its associated
@ -88,7 +87,7 @@ import java.io.IOException;
* per-message operations are returned in an instance of the MessageProp
* class, which is used as an argument in these calls.</dl>
*/
class GSSContextImpl implements GSSContext {
class GSSContextImpl implements ExtendedGSSContext {
private GSSManagerImpl gssManager = null;
@ -630,4 +629,16 @@ class GSSContextImpl implements GSSContext {
srcName = null;
targName = null;
}
@Override
public Object inquireSecContext(InquireType type) throws GSSException {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(new InquireSecContextPermission(type.toString()));
}
if (mechCtxt == null) {
throw new GSSException(GSSException.NO_CONTEXT);
}
return mechCtxt.inquireSecContext(type);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,12 +25,14 @@
package sun.security.jgss.krb5;
import com.sun.security.jgss.AuthorizationDataEntry;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import sun.security.krb5.*;
import java.net.InetAddress;
import sun.security.krb5.internal.AuthorizationData;
import sun.security.krb5.internal.KerberosTime;
class InitSecContextToken extends InitialToken {
@ -59,6 +61,9 @@ class InitSecContextToken extends InitialToken {
Checksum checksum = gssChecksum.getChecksum();
context.setTktFlags(serviceTicket.getFlags());
context.setAuthTime(
new KerberosTime(serviceTicket.getAuthTime()).toString());
apReq = new KrbApReq(serviceTicket,
mutualRequired,
useSubkey,
@ -143,6 +148,21 @@ class InitSecContextToken extends InitialToken {
// Use the same sequence number as the peer
// (Behaviour exhibited by the Windows SSPI server)
context.resetMySequenceNumber(peerSeqNumber);
context.setAuthTime(
new KerberosTime(apReq.getCreds().getAuthTime()).toString());
context.setTktFlags(apReq.getCreds().getFlags());
AuthorizationData ad = apReq.getCreds().getAuthzData();
if (ad == null) {
context.setAuthzData(null);
} else {
AuthorizationDataEntry[] authzData =
new AuthorizationDataEntry[ad.count()];
for (int i=0; i<ad.count(); i++) {
authzData[i] = new AuthorizationDataEntry(
ad.item(i).adType, ad.item(i).adData);
}
context.setAuthzData(authzData);
}
}
public final KrbApReq getKrbApReq() {

View File

@ -25,6 +25,7 @@
package sun.security.jgss.krb5;
import com.sun.security.jgss.InquireType;
import org.ietf.jgss.*;
import sun.misc.HexDumpEncoder;
import sun.security.jgss.GSSUtil;
@ -38,6 +39,7 @@ import java.io.IOException;
import java.security.Provider;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.Key;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import javax.crypto.Cipher;
@ -1283,4 +1285,81 @@ class Krb5Context implements GSSContextSpi {
// Currently used by InitialToken only
return caller;
}
/**
* The session key returned by inquireSecContext(KRB5_INQ_SSPI_SESSION_KEY)
*/
static class KerberosSessionKey implements Key {
private final EncryptionKey key;
KerberosSessionKey(EncryptionKey key) {
this.key = key;
}
@Override
public String getAlgorithm() {
return Integer.toString(key.getEType());
}
@Override
public String getFormat() {
return "RAW";
}
@Override
public byte[] getEncoded() {
return key.getBytes().clone();
}
@Override
public String toString() {
return "Kerberos session key: etype: " + key.getEType() + "\n" +
new sun.misc.HexDumpEncoder().encodeBuffer(key.getBytes());
}
}
/**
* Return the mechanism-specific attribute associated with {@code type}.
*/
public Object inquireSecContext(InquireType type)
throws GSSException {
if (!isEstablished()) {
throw new GSSException(GSSException.NO_CONTEXT, -1,
"Security context not established.");
}
switch (type) {
case KRB5_GET_SESSION_KEY:
return new KerberosSessionKey(key);
case KRB5_GET_TKT_FLAGS:
return tktFlags.clone();
case KRB5_GET_AUTHZ_DATA:
if (isInitiator()) {
throw new GSSException(GSSException.UNAVAILABLE, -1,
"AuthzData not available on initiator side.");
} else {
return (authzData==null)?null:authzData.clone();
}
case KRB5_GET_AUTHTIME:
return authTime;
}
throw new GSSException(GSSException.UNAVAILABLE, -1,
"Inquire type not supported.");
}
// Helpers for inquireSecContext
private boolean[] tktFlags;
private String authTime;
private com.sun.security.jgss.AuthorizationDataEntry[] authzData;
public void setTktFlags(boolean[] tktFlags) {
this.tktFlags = tktFlags;
}
public void setAuthTime(String authTime) {
this.authTime = authTime;
}
public void setAuthzData(com.sun.security.jgss.AuthorizationDataEntry[] authzData) {
this.authzData = authzData;
}
}

View File

@ -1,5 +1,5 @@
/*
* Portions Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -46,6 +46,7 @@ import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Provider;
import com.sun.security.jgss.*;
/**
* This interface is implemented by a mechanism specific instance of a GSS
@ -265,7 +266,6 @@ public interface GSSContextSpi {
* @param msgPro on input it contains the requested qop and
* confidentiality state, on output, the applied values
* @exception GSSException may be thrown
* @see MessageInfo
* @see unwrap
*/
public void wrap(InputStream is, OutputStream os, MessageProp msgProp)
@ -315,7 +315,6 @@ public interface GSSContextSpi {
* @param msgProp will contain the applied qop and confidentiality
* of the input token and any informatory status values
* @exception GSSException may be thrown
* @see MessageInfo
* @see wrap
*/
public void unwrap(InputStream is, OutputStream os,
@ -403,4 +402,15 @@ public interface GSSContextSpi {
* @exception GSSException may be thrown
*/
public void dispose() throws GSSException;
/**
* Return the mechanism-specific attribute associated with (@code type}.
*
* @param type the type of the attribute requested
* @return the attribute
* @throws GSSException see {@link ExtendedGSSContext#inquireSecContext}
* for details
*/
public Object inquireSecContext(InquireType type)
throws GSSException;
}

View File

@ -25,10 +25,10 @@
package sun.security.jgss.spnego;
import com.sun.security.jgss.ExtendedGSSContext;
import com.sun.security.jgss.InquireType;
import java.io.*;
import java.security.Provider;
import java.util.List;
import java.util.ArrayList;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.jgss.spi.*;
@ -1185,4 +1185,22 @@ public class SpNegoContext implements GSSContextSpi {
return ("Unknown state " + state);
}
}
/**
* Retrieve attribute of the context for {@code type}.
*/
public Object inquireSecContext(InquireType type)
throws GSSException {
if (mechContext == null) {
throw new GSSException(GSSException.NO_CONTEXT, -1,
"Underlying mech not established.");
}
if (mechContext instanceof ExtendedGSSContext) {
return ((ExtendedGSSContext)mechContext).inquireSecContext(type);
} else {
throw new GSSException(GSSException.BAD_MECH, -1,
"inquireSecContext not supported by underlying mech.");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2005 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2005-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -36,6 +36,7 @@ import sun.security.util.ObjectIdentifier;
import sun.security.jgss.spnego.NegTokenInit;
import sun.security.jgss.spnego.NegTokenTarg;
import javax.security.auth.kerberos.DelegationPermission;
import com.sun.security.jgss.InquireType;
import java.io.*;
@ -615,4 +616,10 @@ class NativeGSSContext implements GSSContextSpi {
protected void finalize() throws Throwable {
dispose();
}
public Object inquireSecContext(InquireType type)
throws GSSException {
throw new GSSException(GSSException.UNAVAILABLE, -1,
"Inquire type not supported.");
}
}

View File

@ -1,5 +1,5 @@
/*
* Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -63,12 +63,29 @@ public class Credentials {
KerberosTime renewTill;
HostAddresses cAddr;
EncryptionKey serviceKey;
AuthorizationData authzData;
private static boolean DEBUG = Krb5.DEBUG;
private static CredentialsCache cache;
static boolean alreadyLoaded = false;
private static boolean alreadyTried = false;
private static native Credentials acquireDefaultNativeCreds();
public Credentials(Ticket new_ticket,
PrincipalName new_client,
PrincipalName new_server,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
KerberosTime new_startTime,
KerberosTime new_endTime,
KerberosTime renewTill,
HostAddresses cAddr,
AuthorizationData authzData) {
this(new_ticket, new_client, new_server, new_key, new_flags,
authTime, new_startTime, new_endTime, renewTill, cAddr);
this.authzData = authzData;
}
public Credentials(Ticket new_ticket,
PrincipalName new_client,
PrincipalName new_server,
@ -213,6 +230,9 @@ public class Credentials {
return flags;
}
public AuthorizationData getAuthzData() {
return authzData;
}
/**
* Checks if the service ticket returned by the KDC has the OK-AS-DELEGATE
* flag set

Some files were not shown because too many files have changed in this diff Show More