Merge
This commit is contained in:
commit
a6ce90c2bb
@ -43,3 +43,4 @@ e01380cd1de4ce048b87d059d238e5ab5e341947 jdk7-b65
|
||||
6bad5e3fe50337d95b1416d744780d65bc570da6 jdk7-b66
|
||||
c4523c6f82048f420bf0d57c4cd47976753b7d2c jdk7-b67
|
||||
e1b972ff53cd58f825791f8ed9b2deffd16e768c jdk7-b68
|
||||
82e6c820c51ac27882b77755d42efefdbf1dcda0 jdk7-b69
|
||||
|
@ -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
|
||||
|
@ -43,3 +43,4 @@ d20e45cd539f20405ff843652069cfd7550c5ab3 jdk7-b63
|
||||
a821e059a961bcb02830280d51f6dd030425c066 jdk7-b66
|
||||
a12ea7c7b497b4ba7830550095ef633bd6f43971 jdk7-b67
|
||||
5182bcc9c60cac429d1f7988676cec7320752be3 jdk7-b68
|
||||
8120d308ec4e805c5588b8d9372844d781c4112d jdk7-b69
|
||||
|
@ -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
|
||||
|
@ -43,3 +43,4 @@ ba313800759b678979434d6da8ed3bf49eb8bea4 jdk7-b65
|
||||
57c71ad0341b8b64ed20f81151eb7f06324f8894 jdk7-b66
|
||||
18f526145aea355a9320b724373386fc2170f183 jdk7-b67
|
||||
d07e68298d4e17ebf93d8299e43fcc3ded26472a jdk7-b68
|
||||
54fd4d9232969ea6cd3d236e5ad276183bb0d423 jdk7-b69
|
||||
|
@ -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
|
||||
|
@ -43,3 +43,4 @@ a10eec7a1edf536f39b5828d8623054dbc62c2b7 jdk7-b64
|
||||
22f9d5d5b5fe0f47048f41e6c6e54fee5edad0ec jdk7-b66
|
||||
a033af8d824a408d3ac602205ecdefc128749e1e jdk7-b67
|
||||
83b2a9331383f9db7a49350d4cb13b7635f6b861 jdk7-b68
|
||||
a4ab0d6ded63bed0fd1e5be55d38090e0ee5efb7 jdk7-b69
|
||||
|
@ -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
|
||||
|
@ -1027,6 +1027,9 @@ public abstract class XMLScanner
|
||||
int c = fEntityScanner.peekChar();
|
||||
if (XMLChar.isMarkup(c) || c == ']') {
|
||||
fStringBuffer.append((char)fEntityScanner.scanChar());
|
||||
} else if (c != -1 && isInvalidLiteral(c)) {
|
||||
reportFatalError("InvalidCharInSystemID",
|
||||
new Object[] {Integer.toString(c, 16)});
|
||||
}
|
||||
} while (fEntityScanner.scanLiteral(quote, ident) != quote);
|
||||
fStringBuffer.append(ident);
|
||||
|
@ -43,3 +43,4 @@ a50217eb3ee10b9f9547e0708e5c9625405083ef jdk7-b64
|
||||
bd31b30a5b21f20e42965b1633f18a5c7946d398 jdk7-b66
|
||||
a952aafd5181af953b0ef3010dbd2fcc28460e8a jdk7-b67
|
||||
b23d905cb5d3b382295240d28ab0bfb266b4503c jdk7-b68
|
||||
226b20019b1f020c09ea97d137d98e011ce65d76 jdk7-b69
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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::
|
||||
|
54
jdk/make/sun/security/ec/FILES_c.gmk
Normal file
54
jdk/make/sun/security/ec/FILES_c.gmk
Normal 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
|
||||
|
319
jdk/make/sun/security/ec/Makefile
Normal file
319
jdk/make/sun/security/ec/Makefile
Normal 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
|
37
jdk/make/sun/security/ec/mapfile-vers
Normal file
37
jdk/make/sun/security/ec/mapfile-vers
Normal 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:
|
||||
*;
|
||||
};
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
0x25 U+000a
|
||||
0x25 U+000a
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -60,7 +60,7 @@ public class GIFImageReaderSpi extends ImageReaderSpi {
|
||||
suffixes,
|
||||
MIMETypes,
|
||||
readerClassName,
|
||||
STANDARD_INPUT_TYPE,
|
||||
new Class[] { ImageInputStream.class },
|
||||
writerSpiNames,
|
||||
true,
|
||||
GIFStreamMetadata.nativeMetadataFormatName,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -60,7 +60,7 @@ public class PNGImageReaderSpi extends ImageReaderSpi {
|
||||
suffixes,
|
||||
MIMETypes,
|
||||
readerClassName,
|
||||
STANDARD_INPUT_TYPE,
|
||||
new Class[] { ImageInputStream.class },
|
||||
writerSpiNames,
|
||||
false,
|
||||
null, null,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
54
jdk/src/share/classes/com/sun/security/jgss/InquireType.java
Normal file
54
jdk/src/share/classes/com/sun/security/jgss/InquireType.java
Normal 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
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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} </tt></td>
|
||||
* <td>A direct byte buffer or big byte buffer mapped to a region of a file</td></tr>
|
||||
* <tr><td valign=top><tt>{@link java.nio.MappedByteBuffer} </tt></td>
|
||||
* <td>A direct byte buffer mapped to a region of a file</td></tr>
|
||||
* </table></blockquote>
|
||||
*
|
||||
* <p> The {@link java.nio.channels.FileChannel} class supports the usual
|
||||
|
@ -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>.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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(),
|
||||
|
@ -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>
|
||||
*
|
||||
|
@ -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 > toIndex</tt>
|
||||
* @throws ArrayIndexOutOfBoundsException if <tt>fromIndex < 0</tt> or
|
||||
* <tt>toIndex > 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 > toIndex</tt>
|
||||
* @throws ArrayIndexOutOfBoundsException if <tt>fromIndex < 0</tt> or
|
||||
* <tt>toIndex > 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,
|
||||
|
@ -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();
|
||||
|
895
jdk/src/share/classes/java/util/ComparableTimSort.java
Normal file
895
jdk/src/share/classes/java/util/ComparableTimSort.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
928
jdk/src/share/classes/java/util/TimSort.java
Normal file
928
jdk/src/share/classes/java/util/TimSort.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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 "wait-free"
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
788
jdk/src/share/classes/javax/swing/JLayer.java
Normal file
788
jdk/src/share/classes/javax/swing/JLayer.java
Normal 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<JPanel> layerUI = new LayerUI<JPanel>() {
|
||||
* 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<JPanel> l) {
|
||||
* System.out.println("AWTEvent detected: " + e);
|
||||
* }
|
||||
* };
|
||||
* // create the layer for the panel using our custom layerUI
|
||||
* JLayer<JPanel> layer = new JLayer<JPanel>(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) {
|
||||
}
|
||||
}
|
||||
}
|
@ -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:\\")));
|
||||
}
|
||||
|
||||
|
370
jdk/src/share/classes/javax/swing/plaf/LayerUI.java
Normal file
370
jdk/src/share/classes/javax/swing/plaf/LayerUI.java
Normal 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 < 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
43
jdk/src/share/classes/sun/net/ApplicationProxy.java
Normal file
43
jdk/src/share/classes/sun/net/ApplicationProxy.java
Normal 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);
|
||||
}
|
||||
}
|
80
jdk/src/share/classes/sun/net/util/URLUtil.java
Normal file
80
jdk/src/share/classes/sun/net/util/URLUtil.java
Normal 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();
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
189
jdk/src/share/classes/sun/security/ec/ECDHKeyAgreement.java
Normal file
189
jdk/src/share/classes/sun/security/ec/ECDHKeyAgreement.java
Normal 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;
|
||||
}
|
447
jdk/src/share/classes/sun/security/ec/ECDSASignature.java
Normal file
447
jdk/src/share/classes/sun/security/ec/ECDSASignature.java
Normal 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;
|
||||
}
|
191
jdk/src/share/classes/sun/security/ec/ECKeyPairGenerator.java
Normal file
191
jdk/src/share/classes/sun/security/ec/ECKeyPairGenerator.java
Normal 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);
|
||||
}
|
65
jdk/src/share/classes/sun/security/ec/SunEC.java
Normal file
65
jdk/src/share/classes/sun/security/ec/SunEC.java
Normal 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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
109
jdk/src/share/classes/sun/security/ec/SunECEntries.java
Normal file
109
jdk/src/share/classes/sun/security/ec/SunECEntries.java
Normal 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");
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user