OpenJDK / jdk / jdk10
changeset 7050:525b0c46526a
Merge
author | lana |
---|---|
date | Tue, 09 Nov 2010 22:53:18 -0800 |
parents | e8d03f9ed991 f6ad89ce23f7 |
children | e5798edad254 |
files | jdk/src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java jdk/src/share/classes/sun/java2d/pisces/LineSink.java jdk/src/share/classes/sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java jdk/test/java/nio/channels/AsynchronousDatagramChannel/Basic.java |
diffstat | 275 files changed, 22754 insertions(+), 8211 deletions(-) [+] |
line wrap: on
line diff
--- a/jdk/make/java/java/FILES_java.gmk Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/make/java/java/FILES_java.gmk Tue Nov 09 22:53:18 2010 -0800 @@ -413,6 +413,7 @@ java/io/FilePermission.java \ java/io/Serializable.java \ java/io/Externalizable.java \ + java/io/SerialCallbackContext.java \ java/io/Bits.java \ java/io/ObjectInput.java \ java/io/ObjectInputStream.java \
--- a/jdk/make/java/jli/Makefile Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/make/java/jli/Makefile Tue Nov 09 22:53:18 2010 -0800 @@ -148,14 +148,9 @@ # ifeq ($(PLATFORM), windows) -STATIC_LIBRARY_DIR = $(OBJDIR)/static -STATIC_LIBRARY_NAME = $(LIBPREFIX)$(LIBRARY).lib -STATIC_LIBRARY = $(STATIC_LIBRARY_DIR)/$(STATIC_LIBRARY_NAME) +STATIC_LIBRARY = $(OBJDIR)/static/$(LIBPREFIX)$(LIBRARY).lib -$(STATIC_LIBRARY_DIR): $(OBJDIR) - @$(MKDIR) $(STATIC_LIBRARY_DIR) - -$(STATIC_LIBRARY): $(STATIC_LIBRARY_DIR) +$(STATIC_LIBRARY): $(FILES_o) @$(prep-target) $(LIBEXE) -nologo -out:$@ $(FILES_o)
--- a/jdk/make/java/nio/FILES_java.gmk Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/make/java/nio/FILES_java.gmk Tue Nov 09 22:53:18 2010 -0800 @@ -33,7 +33,6 @@ java/nio/channels/AsynchronousByteChannel.java \ java/nio/channels/AsynchronousChannel.java \ java/nio/channels/AsynchronousChannelGroup.java \ - java/nio/channels/AsynchronousDatagramChannel.java \ java/nio/channels/AsynchronousFileChannel.java \ java/nio/channels/AsynchronousServerSocketChannel.java \ java/nio/channels/AsynchronousSocketChannel.java \ @@ -207,7 +206,6 @@ sun/nio/ch/SelChImpl.java \ sun/nio/ch/ServerSocketAdaptor.java \ sun/nio/ch/ServerSocketChannelImpl.java \ - sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java \ sun/nio/ch/SinkChannelImpl.java \ sun/nio/ch/SocketAdaptor.java \ sun/nio/ch/SocketChannelImpl.java \
--- a/jdk/make/javax/sound/jsoundds/Makefile Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/make/javax/sound/jsoundds/Makefile Tue Nov 09 22:53:18 2010 -0800 @@ -53,7 +53,7 @@ # # Extra cc/linker flags. # -LDLIBS += dsound.lib winmm.lib user32.lib +LDLIBS += dsound.lib winmm.lib user32.lib ole32.lib CPPFLAGS += \ -DUSE_DAUDIO=TRUE \ -I$(SHARE_SRC)/native/com/sun/media/sound \
--- a/jdk/make/netbeans/jmx/build.properties Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/make/netbeans/jmx/build.properties Tue Nov 09 22:53:18 2010 -0800 @@ -48,8 +48,8 @@ jar.jmx.sealed = true jar.jmx.spec.title = JSR 003, 160, 255 - JMX API jar.jmx.spec.version = ${project.spec.version} -jar.jmx.spec.vendor = Sun Microsystems, Inc. -jar.jmx.impl.title = JSR 003, 160, 255 - OpenJDK 7 JMX API +jar.jmx.spec.vendor = Oracle Corporation +jar.jmx.impl.title = JSR 003, 160, 255 - OpenJDK 7 JMX API jar.jmx.impl.vendor = Project OpenJDK javadoc.options=-J-Xmx256m
--- a/jdk/make/sun/javazic/tzdata/VERSION Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/make/sun/javazic/tzdata/VERSION Tue Nov 09 22:53:18 2010 -0800 @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2010l +tzdata2010o
--- a/jdk/make/sun/javazic/tzdata/asia Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/make/sun/javazic/tzdata/asia Tue Nov 09 22:53:18 2010 -0800 @@ -569,8 +569,8 @@ Rule HK 1954 1964 - Mar Sun>=18 3:30 1:00 S Rule HK 1954 only - Oct 31 3:30 0 - Rule HK 1955 1964 - Nov Sun>=1 3:30 0 - -Rule HK 1965 1977 - Apr Sun>=16 3:30 1:00 S -Rule HK 1965 1977 - Oct Sun>=16 3:30 0 - +Rule HK 1965 1976 - Apr Sun>=16 3:30 1:00 S +Rule HK 1965 1976 - Oct Sun>=16 3:30 0 - Rule HK 1973 only - Dec 30 3:30 1:00 S Rule HK 1979 only - May Sun>=8 3:30 1:00 S Rule HK 1979 only - Oct Sun>=16 3:30 0 -
--- a/jdk/make/sun/javazic/tzdata/australasia Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/make/sun/javazic/tzdata/australasia Tue Nov 09 22:53:18 2010 -0800 @@ -306,13 +306,26 @@ # http://www.timeanddate.com/news/time/fiji-dst-ends-march-2010.html # </a> +# From Alexander Krivenyshev (2010-10-24): +# According to Radio Fiji and Fiji Times online, Fiji will end DST 3 +# weeks earlier than expected - on March 6, 2011, not March 27, 2011... +# Here is confirmation from Government of the Republic of the Fiji Islands, +# Ministry of Information (fiji.gov.fj) web site: +# <a href="http://www.fiji.gov.fj/index.php?option=com_content&view=article&id=2608:daylight-savings&catid=71:press-releases&Itemid=155"> +# http://www.fiji.gov.fj/index.php?option=com_content&view=article&id=2608:daylight-savings&catid=71:press-releases&Itemid=155 +# </a> +# or +# <a href="http://www.worldtimezone.com/dst_news/dst_news_fiji04.html"> +# http://www.worldtimezone.com/dst_news/dst_news_fiji04.html +# </a> + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Fiji 1998 1999 - Nov Sun>=1 2:00 1:00 S Rule Fiji 1999 2000 - Feb lastSun 3:00 0 - Rule Fiji 2009 only - Nov 29 2:00 1:00 S Rule Fiji 2010 only - Mar lastSun 3:00 0 - Rule Fiji 2010 only - Oct 24 2:00 1:00 S -Rule Fiji 2011 only - Mar lastSun 3:00 0 - +Rule Fiji 2011 only - Mar Sun>=1 3:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Pacific/Fiji 11:53:40 - LMT 1915 Oct 26 # Suva 12:00 Fiji FJ%sT # Fiji Time @@ -509,11 +522,21 @@ # http://www.parliament.gov.ws/documents/acts/Daylight%20Saving%20Act%20%202009%20%28English%29%20-%20Final%207-7-091.pdf # </a> +# From Raymond Hughes (2010-10-07): +# Please see +# <a href="http://www.mcil.gov.ws"> +# http://www.mcil.gov.ws +# </a>, +# the Ministry of Commerce, Industry and Labour (sideframe) "Last Sunday +# September 2010 (26/09/10) - adjust clocks forward from 12:00 midnight +# to 01:00am and First Sunday April 2011 (03/04/11) - adjust clocks +# backwards from 1:00am to 12:00am" + Zone Pacific/Apia 12:33:04 - LMT 1879 Jul 5 -11:26:56 - LMT 1911 -11:30 - SAMT 1950 # Samoa Time -11:00 - WST 2010 Sep 26 - -11:00 1:00 WSDT 2011 Apr 3 + -11:00 1:00 WSDT 2011 Apr 3 1:00 -11:00 - WST # Solomon Is
--- a/jdk/make/sun/javazic/tzdata/zone.tab Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/make/sun/javazic/tzdata/zone.tab Tue Nov 09 22:53:18 2010 -0800 @@ -63,7 +63,7 @@ AQ -6736+06253 Antarctica/Mawson Mawson Station, Holme Bay AQ -6835+07758 Antarctica/Davis Davis Station, Vestfold Hills AQ -6617+11031 Antarctica/Casey Casey Station, Bailey Peninsula -AQ -7824+10654 Antarctica/Vostok Vostok Station, S Magnetic Pole +AQ -7824+10654 Antarctica/Vostok Vostok Station, Lake Vostok AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville Station, Terre Adelie AQ -690022+0393524 Antarctica/Syowa Syowa Station, E Ongul I AQ -5430+15857 Antarctica/Macquarie Macquarie Island Station, Macquarie Island
--- a/jdk/src/share/bin/java.c Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/bin/java.c Tue Nov 09 22:53:18 2010 -0800 @@ -355,7 +355,6 @@ JavaVM *vm = 0; JNIEnv *env = 0; - jstring mainClassName; jclass mainClass; jmethodID mainID; jobjectArray mainArgs;
--- a/jdk/src/share/bin/parse_manifest.c Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/bin/parse_manifest.c Tue Nov 09 22:53:18 2010 -0800 @@ -72,7 +72,7 @@ if (entry->how == STORED) { *(char *)((size_t)in + entry->csize) = '\0'; if (size_out) { - *size_out = entry->csize; + *size_out = (int)entry->csize; } return (in); } else if (entry->how == DEFLATED) { @@ -103,7 +103,7 @@ return (NULL); } if (size_out) { - *size_out = entry->isize; + *size_out = (int)entry->isize; } return (out); } else @@ -317,7 +317,7 @@ * manifest. If so, build the entry record from the data found in * the header located and return success. */ - if (CENNAM(p) == JLI_StrLen(file_name) && + if ((size_t)CENNAM(p) == JLI_StrLen(file_name) && memcmp((p + CENHDR), file_name, JLI_StrLen(file_name)) == 0) { if (lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (off_t)0) { free(buffer); @@ -606,8 +606,5 @@ } free(mp); close(fd); - if (rc == 0) - return (0); - else - return (-2); + return (rc == 0) ? 0 : -2; }
--- a/jdk/src/share/bin/wildcard.c Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/bin/wildcard.c Tue Nov 09 22:53:18 2010 -0800 @@ -290,12 +290,12 @@ char *path; char *p; for (i = 0, size = 1; i < fl->size; i++) - size += JLI_StrLen(fl->files[i]) + 1; + size += (int)JLI_StrLen(fl->files[i]) + 1; path = JLI_MemAlloc(size); for (i = 0, p = path; i < fl->size; i++) { - int len = JLI_StrLen(fl->files[i]); + int len = (int)JLI_StrLen(fl->files[i]); if (i > 0) *p++ = sep; memcpy(p, fl->files[i], len); p += len; @@ -309,7 +309,7 @@ FileList_split(const char *path, char sep) { const char *p, *q; - int len = JLI_StrLen(path); + int len = (int)JLI_StrLen(path); int count; FileList fl; for (count = 1, p = path; p < path + len; p++) @@ -330,7 +330,7 @@ static int isJarFileName(const char *filename) { - int len = JLI_StrLen(filename); + int len = (int)JLI_StrLen(filename); return (len >= 4) && (filename[len - 4] == '.') && (equal(filename + len - 3, "jar") || @@ -342,8 +342,8 @@ static char * wildcardConcat(const char *wildcard, const char *basename) { - int wildlen = JLI_StrLen(wildcard); - int baselen = JLI_StrLen(basename); + int wildlen = (int)JLI_StrLen(wildcard); + int baselen = (int)JLI_StrLen(basename); char *filename = (char *) JLI_MemAlloc(wildlen + baselen); /* Replace the trailing '*' with basename */ memcpy(filename, wildcard, wildlen-1); @@ -369,7 +369,7 @@ static int isWildcard(const char *filename) { - int len = JLI_StrLen(filename); + int len = (int)JLI_StrLen(filename); return (len > 0) && (filename[len - 1] == '*') && (len == 1 || IS_FILE_SEPARATOR(filename[len - 2])) &&
--- a/jdk/src/share/classes/com/sun/crypto/provider/AESCrypt.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/AESCrypt.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. 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 @@ -253,7 +253,8 @@ for (j = 0; j < 8; j++) { if (AA[i][j] != 0) { AA[i][j] = (byte) - alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255]; + alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) + % 255]; } } for (t = 0; t < 4; t++) {
--- a/jdk/src/share/classes/com/sun/crypto/provider/ARCFOURCipher.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/ARCFOURCipher.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. 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 @@ -92,7 +92,8 @@ } // core crypt code. OFB style, so works for both encryption and decryption - private void crypt(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) { + private void crypt(byte[] in, int inOfs, int inLen, byte[] out, + int outOfs) { if (is < 0) { // doFinal() was called, need to reset the cipher to initial state init(lastKey);
--- a/jdk/src/share/classes/com/sun/crypto/provider/DESedeCipher.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/DESedeCipher.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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 @@ -31,8 +31,8 @@ import javax.crypto.spec.*; /** - * This class implements the DESede algorithm (DES-EDE, tripleDES) in its various - * modes (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, + * This class implements the DESede algorithm (DES-EDE, tripleDES) in + * its various modes (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, * <code>CBC</code>, <code>PCBC</code>) and padding schemes * (<code>PKCS5Padding</code>, <code>NoPadding</code>, * <code>ISO10126Padding</code>).
--- a/jdk/src/share/classes/com/sun/crypto/provider/DHPrivateKey.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/DHPrivateKey.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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 @@ -190,7 +190,8 @@ ike.initCause(e); throw ike; } catch (IOException e) { - InvalidKeyException ike = new InvalidKeyException("Error parsing key encoding: " + e.getMessage()); + InvalidKeyException ike = new InvalidKeyException( + "Error parsing key encoding: " + e.getMessage()); ike.initCause(e); throw ike; } @@ -300,7 +301,8 @@ DerInputStream in = new DerInputStream(this.key); this.x = in.getBigInteger(); } catch (IOException e) { - InvalidKeyException ike = new InvalidKeyException("Error parsing key encoding: " + e.getMessage()); + InvalidKeyException ike = new InvalidKeyException( + "Error parsing key encoding: " + e.getMessage()); ike.initCause(e); throw ike; }
--- a/jdk/src/share/classes/com/sun/crypto/provider/DHPublicKey.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/DHPublicKey.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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 @@ -180,7 +180,8 @@ throw new InvalidKeyException("Private-value length too big"); } catch (IOException e) { - throw new InvalidKeyException("Error parsing key encoding: " + e.toString()); + throw new InvalidKeyException( + "Error parsing key encoding: " + e.toString()); } } @@ -281,7 +282,8 @@ DerInputStream in = new DerInputStream(this.key); this.y = in.getBigInteger(); } catch (IOException e) { - throw new InvalidKeyException("Error parsing key encoding: " + e.toString()); + throw new InvalidKeyException( + "Error parsing key encoding: " + e.toString()); } }
--- a/jdk/src/share/classes/com/sun/crypto/provider/JceKeyStore.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/JceKeyStore.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. 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 @@ -764,7 +764,8 @@ cf = (CertificateFactory)cfs.get(certType); } else { // create new certificate factory - cf = CertificateFactory.getInstance(certType); + cf = CertificateFactory.getInstance( + certType); // store the certificate factory so we can // reuse it later cfs.put(certType, cf); @@ -863,8 +864,9 @@ dis.readFully(actual); for (int i = 0; i < computed.length; i++) { if (computed[i] != actual[i]) { - throw new IOException("Keystore was tampered with, or " - + "password was incorrect"); + throw new IOException( + "Keystore was tampered with, or " + + "password was incorrect"); } } }
--- a/jdk/src/share/classes/com/sun/crypto/provider/OAEPParameters.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/OAEPParameters.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. 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 @@ -139,7 +139,8 @@ if (!val.getOID().equals((Object) OID_MGF1)) { throw new IOException("Only MGF1 mgf is supported"); } - AlgorithmId params = AlgorithmId.parse(new DerValue(val.getEncodedParams())); + AlgorithmId params = AlgorithmId.parse( + new DerValue(val.getEncodedParams())); String mgfDigestName = convertToStandardName(params.getName()); if (mgfDigestName.equals("SHA-1")) { mgfSpec = MGF1ParameterSpec.SHA1; @@ -150,7 +151,8 @@ } else if (mgfDigestName.equals("SHA-512")) { mgfSpec = MGF1ParameterSpec.SHA512; } else { - throw new IOException("Unrecognized message digest algorithm"); + throw new IOException( + "Unrecognized message digest algorithm"); } } else if (data.isContextSpecific((byte) 0x02)) { // pSource algid
--- a/jdk/src/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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 @@ -121,8 +121,8 @@ this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength); } - private static byte[] deriveKey(final Mac prf, final byte[] password, byte[] salt, - int iterCount, int keyLengthInBit) { + private static byte[] deriveKey(final Mac prf, final byte[] password, + byte[] salt, int iterCount, int keyLengthInBit) { int keyLength = keyLengthInBit/8; byte[] key = new byte[keyLength]; try { @@ -155,8 +155,9 @@ if (this == obj) return true; if (this.getClass() != obj.getClass()) return false; SecretKey sk = (SecretKey)obj; - return prf.getAlgorithm().equalsIgnoreCase(sk.getAlgorithm()) && - Arrays.equals(password, sk.getEncoded()); + return prf.getAlgorithm().equalsIgnoreCase( + sk.getAlgorithm()) && + Arrays.equals(password, sk.getEncoded()); } }; prf.init(macKey);
--- a/jdk/src/share/classes/com/sun/crypto/provider/PKCS12PBECipherCore.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/PKCS12PBECipherCore.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. 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 @@ -206,7 +206,8 @@ (algo.equalsIgnoreCase("RC2")?"RC2_40":algo), "SunJCE"); } catch (GeneralSecurityException gse) { // should never happen - throw new RuntimeException("SunJCE provider is not configured properly"); + throw new RuntimeException( + "SunJCE provider is not configured properly"); } try { params.init(pbeSpec); @@ -316,7 +317,8 @@ try { paramSpec = params.getParameterSpec(PBEParameterSpec.class); } catch (InvalidParameterSpecException ipse) { - throw new InvalidAlgorithmParameterException("requires PBE parameters"); + throw new InvalidAlgorithmParameterException( + "requires PBE parameters"); } } implInit(opmode, key, paramSpec, random);
--- a/jdk/src/share/classes/com/sun/crypto/provider/SunJCE.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/SunJCE.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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 @@ -423,15 +423,31 @@ /* * SSL/TLS mechanisms + * + * These are strictly internal implementations and may + * be changed at any time. These names were chosen + * because PKCS11/SunPKCS11 does not yet have TLS1.2 + * mechanisms, and it will cause calls to come here. */ put("KeyGenerator.SunTlsPrf", - "com.sun.crypto.provider.TlsPrfGenerator"); + "com.sun.crypto.provider.TlsPrfGenerator$V10"); + put("KeyGenerator.SunTls12Prf", + "com.sun.crypto.provider.TlsPrfGenerator$V12"); + + put("KeyGenerator.SunTlsMasterSecret", + "com.sun.crypto.provider.TlsMasterSecretGenerator"); + put("Alg.Alias.KeyGenerator.SunTls12MasterSecret", + "SunTlsMasterSecret"); + + put("KeyGenerator.SunTlsKeyMaterial", + "com.sun.crypto.provider.TlsKeyMaterialGenerator"); + put("Alg.Alias.KeyGenerator.SunTls12KeyMaterial", + "SunTlsKeyMaterial"); + put("KeyGenerator.SunTlsRsaPremasterSecret", - "com.sun.crypto.provider.TlsRsaPremasterSecretGenerator"); - put("KeyGenerator.SunTlsMasterSecret", - "com.sun.crypto.provider.TlsMasterSecretGenerator"); - put("KeyGenerator.SunTlsKeyMaterial", - "com.sun.crypto.provider.TlsKeyMaterialGenerator"); + "com.sun.crypto.provider.TlsRsaPremasterSecretGenerator"); + put("Alg.Alias.KeyGenerator.SunTls12RsaPremasterSecret", + "SunTlsRsaPremasterSecret"); return null; }
--- a/jdk/src/share/classes/com/sun/crypto/provider/TlsKeyMaterialGenerator.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/TlsKeyMaterialGenerator.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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 @@ -65,12 +65,14 @@ } this.spec = (TlsKeyMaterialParameterSpec)params; if ("RAW".equals(spec.getMasterSecret().getFormat()) == false) { - throw new InvalidAlgorithmParameterException("Key format must be RAW"); + throw new InvalidAlgorithmParameterException( + "Key format must be RAW"); } - protocolVersion = (spec.getMajorVersion() << 8) | spec.getMinorVersion(); - if ((protocolVersion < 0x0300) || (protocolVersion > 0x0302)) { - throw new InvalidAlgorithmParameterException - ("Only SSL 3.0, TLS 1.0, and TLS 1.1 supported"); + protocolVersion = (spec.getMajorVersion() << 8) + | spec.getMinorVersion(); + if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) { + throw new InvalidAlgorithmParameterException( + "Only SSL 3.0, TLS 1.0/1.1/1.2 supported"); } } @@ -80,8 +82,8 @@ protected SecretKey engineGenerateKey() { if (spec == null) { - throw new IllegalStateException - ("TlsKeyMaterialGenerator must be initialized"); + throw new IllegalStateException( + "TlsKeyMaterialGenerator must be initialized"); } try { return engineGenerateKey0(); @@ -99,8 +101,8 @@ SecretKey clientMacKey = null; SecretKey serverMacKey = null; SecretKey clientCipherKey = null; + SecretKey serverCipherKey = null; IvParameterSpec clientIv = null; - SecretKey serverCipherKey = null; IvParameterSpec serverIv = null; int macLength = spec.getMacKeyLength(); @@ -109,21 +111,33 @@ int keyLength = spec.getCipherKeyLength(); int ivLength = spec.getIvLength(); - int keyBlockLen = macLength + keyLength + (isExportable ? 0 : ivLength); + int keyBlockLen = macLength + keyLength + + (isExportable ? 0 : ivLength); keyBlockLen <<= 1; byte[] keyBlock = new byte[keyBlockLen]; - MessageDigest md5 = MessageDigest.getInstance("MD5"); - MessageDigest sha = MessageDigest.getInstance("SHA1"); + // These may be used again later for exportable suite calculations. + MessageDigest md5 = null; + MessageDigest sha = null; // generate key block - if (protocolVersion >= 0x0301) { - // TLS + if (protocolVersion >= 0x0303) { + // TLS 1.2 byte[] seed = concat(serverRandom, clientRandom); - keyBlock = doPRF(masterSecret, LABEL_KEY_EXPANSION, seed, + keyBlock = doTLS12PRF(masterSecret, LABEL_KEY_EXPANSION, seed, + keyBlockLen, spec.getPRFHashAlg(), + spec.getPRFHashLength(), spec.getPRFBlockSize()); + } else if (protocolVersion >= 0x0301) { + // TLS 1.0/1.1 + md5 = MessageDigest.getInstance("MD5"); + sha = MessageDigest.getInstance("SHA1"); + byte[] seed = concat(serverRandom, clientRandom); + keyBlock = doTLS10PRF(masterSecret, LABEL_KEY_EXPANSION, seed, keyBlockLen, md5, sha); } else { // SSL + md5 = MessageDigest.getInstance("MD5"); + sha = MessageDigest.getInstance("SHA1"); keyBlock = new byte[keyBlockLen]; byte[] tmp = new byte[20]; @@ -169,6 +183,7 @@ String alg = spec.getCipherAlgorithm(); + // cipher keys byte[] clientKeyBytes = new byte[keyLength]; System.arraycopy(keyBlock, ofs, clientKeyBytes, 0, keyLength); ofs += keyLength; @@ -182,6 +197,7 @@ clientCipherKey = new SecretKeySpec(clientKeyBytes, alg); serverCipherKey = new SecretKeySpec(serverKeyBytes, alg); + // IV keys if needed. if (ivLength != 0) { tmp = new byte[ivLength]; @@ -194,21 +210,28 @@ serverIv = new IvParameterSpec(tmp); } } else { + // if exportable suites, calculate the alternate // cipher key expansion and IV generation - if (protocolVersion >= 0x0301) { + if (protocolVersion >= 0x0302) { + // TLS 1.1+ + throw new RuntimeException( + "Internal Error: TLS 1.1+ should not be negotiating" + + "exportable ciphersuites"); + } else if (protocolVersion == 0x0301) { + // TLS 1.0 byte[] seed = concat(clientRandom, serverRandom); - tmp = doPRF(clientKeyBytes, LABEL_CLIENT_WRITE_KEY, seed, + tmp = doTLS10PRF(clientKeyBytes, LABEL_CLIENT_WRITE_KEY, seed, expandedKeyLength, md5, sha); clientCipherKey = new SecretKeySpec(tmp, alg); - tmp = doPRF(serverKeyBytes, LABEL_SERVER_WRITE_KEY, seed, + tmp = doTLS10PRF(serverKeyBytes, LABEL_SERVER_WRITE_KEY, seed, expandedKeyLength, md5, sha); serverCipherKey = new SecretKeySpec(tmp, alg); if (ivLength != 0) { tmp = new byte[ivLength]; - byte[] block = doPRF(null, LABEL_IV_BLOCK, seed, + byte[] block = doTLS10PRF(null, LABEL_IV_BLOCK, seed, ivLength << 1, md5, sha); System.arraycopy(block, 0, tmp, 0, ivLength); clientIv = new IvParameterSpec(tmp); @@ -216,6 +239,7 @@ serverIv = new IvParameterSpec(tmp); } } else { + // SSLv3 tmp = new byte[expandedKeyLength]; md5.update(clientKeyBytes);
--- a/jdk/src/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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 @@ -64,12 +64,14 @@ } this.spec = (TlsMasterSecretParameterSpec)params; if ("RAW".equals(spec.getPremasterSecret().getFormat()) == false) { - throw new InvalidAlgorithmParameterException("Key format must be RAW"); + throw new InvalidAlgorithmParameterException( + "Key format must be RAW"); } - protocolVersion = (spec.getMajorVersion() << 8) | spec.getMinorVersion(); - if ((protocolVersion < 0x0300) || (protocolVersion > 0x0302)) { - throw new InvalidAlgorithmParameterException - ("Only SSL 3.0, TLS 1.0, and TLS 1.1 supported"); + protocolVersion = (spec.getMajorVersion() << 8) + | spec.getMinorVersion(); + if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) { + throw new InvalidAlgorithmParameterException( + "Only SSL 3.0, TLS 1.0/1.1/1.2 supported"); } } @@ -79,8 +81,8 @@ protected SecretKey engineGenerateKey() { if (spec == null) { - throw new IllegalStateException - ("TlsMasterSecretGenerator must be initialized"); + throw new IllegalStateException( + "TlsMasterSecretGenerator must be initialized"); } SecretKey premasterKey = spec.getPremasterSecret(); byte[] premaster = premasterKey.getEncoded(); @@ -103,7 +105,11 @@ if (protocolVersion >= 0x0301) { byte[] seed = concat(clientRandom, serverRandom); - master = doPRF(premaster, LABEL_MASTER_SECRET, seed, 48); + master = ((protocolVersion >= 0x0303) ? + doTLS12PRF(premaster, LABEL_MASTER_SECRET, seed, 48, + spec.getPRFHashAlg(), spec.getPRFHashLength(), + spec.getPRFBlockSize()) : + doTLS10PRF(premaster, LABEL_MASTER_SECRET, seed, 48)); } else { master = new byte[48]; MessageDigest md5 = MessageDigest.getInstance("MD5"); @@ -124,7 +130,8 @@ } - return new TlsMasterSecretKey(master, premasterMajor, premasterMinor); + return new TlsMasterSecretKey(master, premasterMajor, + premasterMinor); } catch (NoSuchAlgorithmException e) { throw new ProviderException(e); } catch (DigestException e) {
--- a/jdk/src/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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 @@ -37,11 +37,15 @@ /** * KeyGenerator implementation for the TLS PRF function. + * <p> + * This class duplicates the HMAC functionality (RFC 2104) with + * performance optimizations (e.g. XOR'ing keys with padding doesn't + * need to be redone for each HMAC operation). * * @author Andreas Sterbenz * @since 1.6 */ -public final class TlsPrfGenerator extends KeyGeneratorSpi { +abstract class TlsPrfGenerator extends KeyGeneratorSpi { // magic constants and utility functions, also used by other files // in this package @@ -69,8 +73,10 @@ * TLS HMAC "inner" and "outer" padding. This isn't a function * of the digest algorithm. */ - private static final byte[] HMAC_ipad = genPad((byte)0x36, 64); - private static final byte[] HMAC_opad = genPad((byte)0x5c, 64); + private static final byte[] HMAC_ipad64 = genPad((byte)0x36, 64); + private static final byte[] HMAC_ipad128 = genPad((byte)0x36, 128); + private static final byte[] HMAC_opad64 = genPad((byte)0x5c, 64); + private static final byte[] HMAC_opad128 = genPad((byte)0x5c, 128); // SSL3 magic mix constants ("A", "BB", "CCC", ...) final static byte[][] SSL3_CONST = genConst(); @@ -123,8 +129,8 @@ this.spec = (TlsPrfParameterSpec)params; SecretKey key = spec.getSecret(); if ((key != null) && ("RAW".equals(key.getFormat()) == false)) { - throw new InvalidAlgorithmParameterException - ("Key encoding format must be RAW"); + throw new InvalidAlgorithmParameterException( + "Key encoding format must be RAW"); } } @@ -132,17 +138,21 @@ throw new InvalidParameterException(MSG); } - protected SecretKey engineGenerateKey() { + SecretKey engineGenerateKey0(boolean tls12) { if (spec == null) { - throw new IllegalStateException - ("TlsPrfGenerator must be initialized"); + throw new IllegalStateException( + "TlsPrfGenerator must be initialized"); } SecretKey key = spec.getSecret(); byte[] secret = (key == null) ? null : key.getEncoded(); try { byte[] labelBytes = spec.getLabel().getBytes("UTF8"); int n = spec.getOutputLength(); - byte[] prfBytes = doPRF(secret, labelBytes, spec.getSeed(), n); + byte[] prfBytes = (tls12 ? + doTLS12PRF(secret, labelBytes, spec.getSeed(), n, + spec.getPRFHashAlg(), spec.getPRFHashLength(), + spec.getPRFBlockSize()) : + doTLS10PRF(secret, labelBytes, spec.getSeed(), n)); return new SecretKeySpec(prfBytes, "TlsPrf"); } catch (GeneralSecurityException e) { throw new ProviderException("Could not generate PRF", e); @@ -151,16 +161,67 @@ } } - static final byte[] doPRF(byte[] secret, byte[] labelBytes, byte[] seed, - int outputLength) throws NoSuchAlgorithmException, DigestException { + static byte[] doTLS12PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength, + String prfHash, int prfHashLength, int prfBlockSize) + throws NoSuchAlgorithmException, DigestException { + if (prfHash == null) { + throw new NoSuchAlgorithmException("Unspecified PRF algorithm"); + } + MessageDigest prfMD = MessageDigest.getInstance(prfHash); + return doTLS12PRF(secret, labelBytes, seed, outputLength, + prfMD, prfHashLength, prfBlockSize); + } + + static byte[] doTLS12PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength, + MessageDigest mdPRF, int mdPRFLen, int mdPRFBlockSize) + throws DigestException { + + if (secret == null) { + secret = B0; + } + + // If we have a long secret, digest it first. + if (secret.length > mdPRFBlockSize) { + secret = mdPRF.digest(secret); + } + + byte[] output = new byte[outputLength]; + byte [] ipad; + byte [] opad; + + switch (mdPRFBlockSize) { + case 64: + ipad = HMAC_ipad64.clone(); + opad = HMAC_opad64.clone(); + break; + case 128: + ipad = HMAC_ipad128.clone(); + opad = HMAC_opad128.clone(); + break; + default: + throw new DigestException("Unexpected block size."); + } + + // P_HASH(Secret, label + seed) + expand(mdPRF, mdPRFLen, secret, 0, secret.length, labelBytes, + seed, output, ipad, opad); + + return output; + } + + static byte[] doTLS10PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength) throws NoSuchAlgorithmException, + DigestException { MessageDigest md5 = MessageDigest.getInstance("MD5"); MessageDigest sha = MessageDigest.getInstance("SHA1"); - return doPRF(secret, labelBytes, seed, outputLength, md5, sha); + return doTLS10PRF(secret, labelBytes, seed, outputLength, md5, sha); } - static final byte[] doPRF(byte[] secret, byte[] labelBytes, byte[] seed, - int outputLength, MessageDigest md5, MessageDigest sha) - throws DigestException { + static byte[] doTLS10PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength, MessageDigest md5, + MessageDigest sha) throws DigestException { /* * Split the secret into two halves S1 and S2 of same length. * S1 is taken from the first half of the secret, S2 from the @@ -183,10 +244,12 @@ byte[] output = new byte[outputLength]; // P_MD5(S1, label + seed) - expand(md5, 16, secret, 0, seclen, labelBytes, seed, output); + expand(md5, 16, secret, 0, seclen, labelBytes, seed, output, + HMAC_ipad64.clone(), HMAC_opad64.clone()); // P_SHA-1(S2, label + seed) - expand(sha, 20, secret, off, seclen, labelBytes, seed, output); + expand(sha, 20, secret, off, seclen, labelBytes, seed, output, + HMAC_ipad64.clone(), HMAC_opad64.clone()); return output; } @@ -201,16 +264,13 @@ * @param seed the seed * @param output the output array */ - private static final void expand(MessageDigest digest, int hmacSize, + private static void expand(MessageDigest digest, int hmacSize, byte[] secret, int secOff, int secLen, byte[] label, byte[] seed, - byte[] output) throws DigestException { + byte[] output, byte[] pad1, byte[] pad2) throws DigestException { /* * modify the padding used, by XORing the key into our copy of that * padding. That's to avoid doing that for each HMAC computation. */ - byte[] pad1 = HMAC_ipad.clone(); - byte[] pad2 = HMAC_opad.clone(); - for (int i = 0; i < secLen; i++) { pad1[i] ^= secret[i + secOff]; pad2[i] ^= secret[i + secOff]; @@ -275,7 +335,34 @@ } remaining -= k; } - } + /** + * A KeyGenerator implementation that supports TLS 1.2. + * <p> + * TLS 1.2 uses a different hash algorithm than 1.0/1.1 for the PRF + * calculations. As of 2010, there is no PKCS11-level support for TLS + * 1.2 PRF calculations, and no known OS's have an internal variant + * we could use. Therefore for TLS 1.2, we are updating JSSE to request + * a different provider algorithm: "SunTls12Prf". If we reused the + * name "SunTlsPrf", the PKCS11 provider would need be updated to + * fail correctly when presented with the wrong version number + * (via Provider.Service.supportsParameters()), and add the + * appropriate supportsParamters() checks into KeyGenerators (not + * currently there). + */ + static public class V12 extends TlsPrfGenerator { + protected SecretKey engineGenerateKey() { + return engineGenerateKey0(true); + } + } + + /** + * A KeyGenerator implementation that supports TLS 1.0/1.1. + */ + static public class V10 extends TlsPrfGenerator { + protected SecretKey engineGenerateKey() { + return engineGenerateKey0(false); + } + } }
--- a/jdk/src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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 @@ -69,8 +69,8 @@ protected SecretKey engineGenerateKey() { if (spec == null) { - throw new IllegalStateException - ("TlsRsaPremasterSecretGenerator must be initialized"); + throw new IllegalStateException( + "TlsRsaPremasterSecretGenerator must be initialized"); } if (random == null) { random = new SecureRandom();
--- a/jdk/src/share/classes/com/sun/jmx/defaults/ServiceName.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/jmx/defaults/ServiceName.java Tue Nov 09 22:53:18 2010 -0800 @@ -76,9 +76,9 @@ /** * The vendor of the JMX specification implemented by this product. * <BR> - * The value is <CODE>Sun Microsystems</CODE>. + * The value is <CODE>Oracle Corporation</CODE>. */ - public static final String JMX_SPEC_VENDOR = "Sun Microsystems"; + public static final String JMX_SPEC_VENDOR = "Oracle Corporation"; /** * The name of this product implementing the JMX specification. @@ -91,7 +91,7 @@ * The name of the vendor of this product implementing the * JMX specification. * <BR> - * The value is <CODE>Sun Microsystems</CODE>. + * The value is <CODE>Oracle Corporation</CODE>. */ - public static final String JMX_IMPL_VENDOR = "Sun Microsystems"; + public static final String JMX_IMPL_VENDOR = "Oracle Corporation"; }
--- a/jdk/src/share/classes/com/sun/jmx/snmp/ServiceName.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/jmx/snmp/ServiceName.java Tue Nov 09 22:53:18 2010 -0800 @@ -27,7 +27,7 @@ /** * Used for storing default values used by SNMP Runtime services. - * <p><b>This API is a Sun Microsystems internal API and is subject + * <p><b>This API is an Oracle Corporation internal API and is subject * to change without notice.</b></p> */ public class ServiceName { @@ -144,16 +144,16 @@ /** * The vendor of the JMX specification implemented by this product. * <BR> - * The value is <CODE>Sun Microsystems</CODE>. + * The value is <CODE>Oracle Corporation</CODE>. */ - public static final String JMX_SPEC_VENDOR = "Sun Microsystems"; + public static final String JMX_SPEC_VENDOR = "Oracle Corporation"; /** * The name of the vendor of this product implementing the JMX specification. * <BR> - * The value is <CODE>Sun Microsystems</CODE>. + * The value is <CODE>Oracle Corporation</CODE>. */ - public static final String JMX_IMPL_VENDOR = "Sun Microsystems"; + public static final String JMX_IMPL_VENDOR = "Oracle Corporation"; /** * The build number of the current product version, of the form <CODE>rXX</CODE>.
--- a/jdk/src/share/classes/com/sun/management/package.html Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/com/sun/management/package.html Tue Nov 09 22:53:18 2010 -0800 @@ -30,7 +30,7 @@ </head> <body bgcolor="white"> -This package contains Sun Microsystem's platform extension to +This package contains Oracle Corporation's platform extension to the implementation of the <a href="{@docRoot}/../../../../api/java/lang/management/package-summary.html"> java.lang.management</a> API and also defines the management
--- a/jdk/src/share/classes/java/awt/BasicStroke.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/awt/BasicStroke.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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,6 +25,8 @@ package java.awt; +import java.beans.ConstructorProperties; + /** * The <code>BasicStroke</code> class defines a basic set of rendering * attributes for the outlines of graphics primitives, which are rendered @@ -183,6 +185,7 @@ * <code>dash</code> is zero * @throws IllegalArgumentException if dash lengths are all zero. */ + @ConstructorProperties({ "lineWidth", "endCap", "lineJoin", "miterLimit", "dashArray", "dashPhase" }) public BasicStroke(float width, int cap, int join, float miterlimit, float dash[], float dash_phase) { if (width < 0.0f) {
--- a/jdk/src/share/classes/java/awt/GradientPaint.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/awt/GradientPaint.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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 @@ -29,6 +29,7 @@ import java.awt.geom.Rectangle2D; import java.awt.geom.AffineTransform; import java.awt.image.ColorModel; +import java.beans.ConstructorProperties; /** * The <code>GradientPaint</code> class provides a way to fill @@ -166,6 +167,7 @@ * @throws NullPointerException if either one of colors or points * is null */ + @ConstructorProperties({ "point1", "color1", "point2", "color2", "cyclic" }) public GradientPaint(Point2D pt1, Color color1, Point2D pt2,
--- a/jdk/src/share/classes/java/awt/GridBagConstraints.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/awt/GridBagConstraints.java Tue Nov 09 22:53:18 2010 -0800 @@ -126,7 +126,7 @@ /** * Place the component centered along the edge of its display area * associated with the start of a page for the current - * <code>ComponentOrienation</code>. Equal to NORTH for horizontal + * {@code ComponentOrientation}. Equal to NORTH for horizontal * orientations. */ public static final int PAGE_START = 19; @@ -134,7 +134,7 @@ /** * Place the component centered along the edge of its display area * associated with the end of a page for the current - * <code>ComponentOrienation</code>. Equal to SOUTH for horizontal + * {@code ComponentOrientation}. Equal to SOUTH for horizontal * orientations. */ public static final int PAGE_END = 20; @@ -142,7 +142,7 @@ /** * Place the component centered along the edge of its display area where * lines of text would normally begin for the current - * <code>ComponentOrienation</code>. Equal to WEST for horizontal, + * {@code ComponentOrientation}. Equal to WEST for horizontal, * left-to-right orientations and EAST for horizontal, right-to-left * orientations. */ @@ -151,7 +151,7 @@ /** * Place the component centered along the edge of its display area where * lines of text would normally end for the current - * <code>ComponentOrienation</code>. Equal to EAST for horizontal, + * {@code ComponentOrientation}. Equal to EAST for horizontal, * left-to-right orientations and WEST for horizontal, right-to-left * orientations. */ @@ -160,7 +160,7 @@ /** * Place the component in the corner of its display area where * the first line of text on a page would normally begin for the current - * <code>ComponentOrienation</code>. Equal to NORTHWEST for horizontal, + * {@code ComponentOrientation}. Equal to NORTHWEST for horizontal, * left-to-right orientations and NORTHEAST for horizontal, right-to-left * orientations. */ @@ -169,7 +169,7 @@ /** * Place the component in the corner of its display area where * the first line of text on a page would normally end for the current - * <code>ComponentOrienation</code>. Equal to NORTHEAST for horizontal, + * {@code ComponentOrientation}. Equal to NORTHEAST for horizontal, * left-to-right orientations and NORTHWEST for horizontal, right-to-left * orientations. */ @@ -178,7 +178,7 @@ /** * Place the component in the corner of its display area where * the last line of text on a page would normally start for the current - * <code>ComponentOrienation</code>. Equal to SOUTHWEST for horizontal, + * {@code ComponentOrientation}. Equal to SOUTHWEST for horizontal, * left-to-right orientations and SOUTHEAST for horizontal, right-to-left * orientations. */ @@ -187,7 +187,7 @@ /** * Place the component in the corner of its display area where * the last line of text on a page would normally end for the current - * <code>ComponentOrienation</code>. Equal to SOUTHEAST for horizontal, + * {@code ComponentOrientation}. Equal to SOUTHEAST for horizontal, * left-to-right orientations and SOUTHWEST for horizontal, right-to-left * orientations. */ @@ -437,7 +437,7 @@ * <code>LINE_START</code>, <code>LINE_END</code>, * <code>FIRST_LINE_START</code>, <code>FIRST_LINE_END</code>, * <code>LAST_LINE_START</code> and <code>LAST_LINE_END</code>. The - * baseline relvative values are: + * baseline relative values are: * <code>BASELINE</code>, <code>BASELINE_LEADING</code>, * <code>BASELINE_TRAILING</code>, * <code>ABOVE_BASELINE</code>, <code>ABOVE_BASELINE_LEADING</code>,
--- a/jdk/src/share/classes/java/awt/LinearGradientPaint.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/awt/LinearGradientPaint.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2010, Oracle and/or its affiliates. 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 @@ -26,10 +26,10 @@ package java.awt; import java.awt.geom.AffineTransform; -import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; +import java.beans.ConstructorProperties; /** * The {@code LinearGradientPaint} class provides a way to fill @@ -271,6 +271,7 @@ * or a {@code fractions} value is less than 0.0 or greater than 1.0, * or the {@code fractions} are not provided in strictly increasing order */ + @ConstructorProperties({ "startPoint", "endPoint", "fractions", "colors", "cycleMethod", "colorSpace", "transform" }) public LinearGradientPaint(Point2D start, Point2D end, float[] fractions, Color[] colors, CycleMethod cycleMethod,
--- a/jdk/src/share/classes/java/awt/RadialGradientPaint.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/awt/RadialGradientPaint.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2010, Oracle and/or its affiliates. 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 @@ -29,6 +29,7 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; +import java.beans.ConstructorProperties; /** * The {@code RadialGradientPaint} class provides a way to fill a shape with @@ -428,6 +429,7 @@ * or a {@code fractions} value is less than 0.0 or greater than 1.0, * or the {@code fractions} are not provided in strictly increasing order */ + @ConstructorProperties({ "centerPoint", "radius", "focusPoint", "fractions", "colors", "cycleMethod", "colorSpace", "transform" }) public RadialGradientPaint(Point2D center, float radius, Point2D focus,
--- a/jdk/src/share/classes/java/awt/Scrollbar.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/awt/Scrollbar.java Tue Nov 09 22:53:18 2010 -0800 @@ -213,7 +213,8 @@ * The size of the <code>Scrollbar</code>'s bubble. * When a scroll bar is used to select a range of values, * the visibleAmount represents the size of this range. - * This is visually indicated by the size of the bubble. + * Depending on platform, this may be visually indicated + * by the size of the bubble. * * @serial * @see #getVisibleAmount @@ -637,6 +638,8 @@ * bubble (also called a thumb or scroll box), usually gives a * visual representation of the relationship of the visible * amount to the range of the scroll bar. + * Note that depending on platform, the value of the visible amount property + * may not be visually indicated by the size of the bubble. * <p> * The scroll bar's bubble may not be displayed when it is not * moveable (e.g. when it takes up the entire length of the @@ -670,6 +673,8 @@ * bubble (also called a thumb or scroll box), usually gives a * visual representation of the relationship of the visible * amount to the range of the scroll bar. + * Note that depending on platform, the value of the visible amount property + * may not be visually indicated by the size of the bubble. * <p> * The scroll bar's bubble may not be displayed when it is not * moveable (e.g. when it takes up the entire length of the
--- a/jdk/src/share/classes/java/awt/Toolkit.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/awt/Toolkit.java Tue Nov 09 22:53:18 2010 -0800 @@ -1831,7 +1831,11 @@ desktopProperties.put(name, newValue); } - desktopPropsSupport.firePropertyChange(name, oldValue, newValue); + // Don't fire change event if old and new values are null. + // It helps to avoid recursive resending of WM_THEMECHANGED + if (oldValue != null || newValue != null) { + desktopPropsSupport.firePropertyChange(name, oldValue, newValue); + } } /**
--- a/jdk/src/share/classes/java/awt/geom/AffineTransform.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/awt/geom/AffineTransform.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. 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 @@ -26,6 +26,7 @@ package java.awt.geom; import java.awt.Shape; +import java.beans.ConstructorProperties; /** * The <code>AffineTransform</code> class represents a 2D affine transform @@ -508,6 +509,7 @@ * @param m12 the Y coordinate translation element of the 3x3 matrix * @since 1.2 */ + @ConstructorProperties({ "scaleX", "shearY", "shearX", "scaleY", "translateX", "translateY" }) public AffineTransform(float m00, float m10, float m01, float m11, float m02, float m12) {
--- a/jdk/src/share/classes/java/io/ObjectInputStream.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/io/ObjectInputStream.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. 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 @@ -265,7 +265,7 @@ * object currently being deserialized and descriptor for current class. * Null when not during readObject upcall. */ - private CallbackContext curContext; + private SerialCallbackContext curContext; /** * Creates an ObjectInputStream that reads from the specified InputStream. @@ -1798,7 +1798,7 @@ private void readExternalData(Externalizable obj, ObjectStreamClass desc) throws IOException { - CallbackContext oldContext = curContext; + SerialCallbackContext oldContext = curContext; curContext = null; try { boolean blocked = desc.hasBlockExternalData(); @@ -1857,10 +1857,10 @@ slotDesc.hasReadObjectMethod() && handles.lookupException(passHandle) == null) { - CallbackContext oldContext = curContext; + SerialCallbackContext oldContext = curContext; try { - curContext = new CallbackContext(obj, slotDesc); + curContext = new SerialCallbackContext(obj, slotDesc); bin.setBlockDataMode(true); slotDesc.invokeReadObject(obj, this); @@ -3505,42 +3505,4 @@ } } - /** - * Context that during upcalls to class-defined readObject methods; holds - * object currently being deserialized and descriptor for current class. - * This context keeps a boolean state to indicate that defaultReadObject - * or readFields has already been invoked with this context or the class's - * readObject method has returned; if true, the getObj method throws - * NotActiveException. - */ - private static class CallbackContext { - private final Object obj; - private final ObjectStreamClass desc; - private final AtomicBoolean used = new AtomicBoolean(); - - public CallbackContext(Object obj, ObjectStreamClass desc) { - this.obj = obj; - this.desc = desc; - } - - public Object getObj() throws NotActiveException { - checkAndSetUsed(); - return obj; - } - - public ObjectStreamClass getDesc() { - return desc; - } - - private void checkAndSetUsed() throws NotActiveException { - if (!used.compareAndSet(false, true)) { - throw new NotActiveException( - "not in readObject invocation or fields already read"); - } - } - - public void setUsed() { - used.set(true); - } - } }
--- a/jdk/src/share/classes/java/io/ObjectOutputStream.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/io/ObjectOutputStream.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. 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 @@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static java.io.ObjectStreamClass.processQueue; +import java.io.SerialCallbackContext; /** * An ObjectOutputStream writes primitive data types and graphs of Java objects @@ -191,10 +192,12 @@ private boolean enableReplace; // values below valid only during upcalls to writeObject()/writeExternal() - /** object currently being serialized */ - private Object curObj; - /** descriptor for current class (null if in writeExternal()) */ - private ObjectStreamClass curDesc; + /** + * Context during upcalls to class-defined writeObject methods; holds + * object currently being serialized and descriptor for current class. + * Null when not during writeObject upcall. + */ + private SerialCallbackContext curContext; /** current PutField object */ private PutFieldImpl curPut; @@ -426,9 +429,11 @@ * <code>OutputStream</code> */ public void defaultWriteObject() throws IOException { - if (curObj == null || curDesc == null) { + if ( curContext == null ) { throw new NotActiveException("not in call to writeObject"); } + Object curObj = curContext.getObj(); + ObjectStreamClass curDesc = curContext.getDesc(); bout.setBlockDataMode(false); defaultWriteFields(curObj, curDesc); bout.setBlockDataMode(true); @@ -446,9 +451,11 @@ */ public ObjectOutputStream.PutField putFields() throws IOException { if (curPut == null) { - if (curObj == null || curDesc == null) { + if (curContext == null) { throw new NotActiveException("not in call to writeObject"); } + Object curObj = curContext.getObj(); + ObjectStreamClass curDesc = curContext.getDesc(); curPut = new PutFieldImpl(curDesc); } return curPut; @@ -1420,17 +1427,15 @@ * writeExternal() method. */ private void writeExternalData(Externalizable obj) throws IOException { - Object oldObj = curObj; - ObjectStreamClass oldDesc = curDesc; PutFieldImpl oldPut = curPut; - curObj = obj; - curDesc = null; curPut = null; if (extendedDebugInfo) { debugInfoStack.push("writeExternal data"); } + SerialCallbackContext oldContext = curContext; try { + curContext = null; if (protocol == PROTOCOL_VERSION_1) { obj.writeExternal(this); } else { @@ -1440,13 +1445,12 @@ bout.writeByte(TC_ENDBLOCKDATA); } } finally { + curContext = oldContext; if (extendedDebugInfo) { debugInfoStack.pop(); } } - curObj = oldObj; - curDesc = oldDesc; curPut = oldPut; } @@ -1461,12 +1465,9 @@ for (int i = 0; i < slots.length; i++) { ObjectStreamClass slotDesc = slots[i].desc; if (slotDesc.hasWriteObjectMethod()) { - Object oldObj = curObj; - ObjectStreamClass oldDesc = curDesc; PutFieldImpl oldPut = curPut; - curObj = obj; - curDesc = slotDesc; curPut = null; + SerialCallbackContext oldContext = curContext; if (extendedDebugInfo) { debugInfoStack.push( @@ -1474,18 +1475,19 @@ slotDesc.getName() + "\")"); } try { + curContext = new SerialCallbackContext(obj, slotDesc); bout.setBlockDataMode(true); slotDesc.invokeWriteObject(obj, this); bout.setBlockDataMode(false); bout.writeByte(TC_ENDBLOCKDATA); } finally { + curContext.setUsed(); + curContext = oldContext; if (extendedDebugInfo) { debugInfoStack.pop(); } } - curObj = oldObj; - curDesc = oldDesc; curPut = oldPut; } else { defaultWriteFields(obj, slotDesc);
--- a/jdk/src/share/classes/java/io/ObjectStreamClass.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/io/ObjectStreamClass.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. 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 @@ -1830,8 +1830,10 @@ private final ObjectStreamField[] fields; /** number of primitive fields */ private final int numPrimFields; - /** unsafe field keys */ - private final long[] keys; + /** unsafe field keys for reading fields - may contain dupes */ + private final long[] readKeys; + /** unsafe fields keys for writing fields - no dupes */ + private final long[] writeKeys; /** field data offsets */ private final int[] offsets; /** field type codes */ @@ -1849,16 +1851,22 @@ FieldReflector(ObjectStreamField[] fields) { this.fields = fields; int nfields = fields.length; - keys = new long[nfields]; + readKeys = new long[nfields]; + writeKeys = new long[nfields]; offsets = new int[nfields]; typeCodes = new char[nfields]; ArrayList<Class<?>> typeList = new ArrayList<Class<?>>(); + Set<Long> usedKeys = new HashSet<Long>(); + for (int i = 0; i < nfields; i++) { ObjectStreamField f = fields[i]; Field rf = f.getField(); - keys[i] = (rf != null) ? + long key = (rf != null) ? unsafe.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET; + readKeys[i] = key; + writeKeys[i] = usedKeys.add(key) ? + key : Unsafe.INVALID_FIELD_OFFSET; offsets[i] = f.getOffset(); typeCodes[i] = f.getTypeCode(); if (!f.isPrimitive()) { @@ -1894,7 +1902,7 @@ * in array should be equal to Unsafe.INVALID_FIELD_OFFSET. */ for (int i = 0; i < numPrimFields; i++) { - long key = keys[i]; + long key = readKeys[i]; int off = offsets[i]; switch (typeCodes[i]) { case 'Z': @@ -1945,7 +1953,7 @@ throw new NullPointerException(); } for (int i = 0; i < numPrimFields; i++) { - long key = keys[i]; + long key = writeKeys[i]; if (key == Unsafe.INVALID_FIELD_OFFSET) { continue; // discard value } @@ -2006,7 +2014,7 @@ switch (typeCodes[i]) { case 'L': case '[': - vals[offsets[i]] = unsafe.getObject(obj, keys[i]); + vals[offsets[i]] = unsafe.getObject(obj, readKeys[i]); break; default: @@ -2027,7 +2035,7 @@ throw new NullPointerException(); } for (int i = numPrimFields; i < fields.length; i++) { - long key = keys[i]; + long key = writeKeys[i]; if (key == Unsafe.INVALID_FIELD_OFFSET) { continue; // discard value }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/io/SerialCallbackContext.java Tue Nov 09 22:53:18 2010 -0800 @@ -0,0 +1,58 @@ + /* + * %W% %E% + * + * Copyright (c) 2006, 2010 Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ + + package java.io; + + /** + * Context during upcalls from object stream to class-defined + * readObject/writeObject methods. + * Holds object currently being deserialized and descriptor for current class. + * + * This context keeps track of the thread it was constructed on, and allows + * only a single call of defaultReadObject, readFields, defaultWriteObject + * or writeFields which must be invoked on the same thread before the class's + * readObject/writeObject method has returned. + * If not set to the current thread, the getObj method throws NotActiveException. + */ + final class SerialCallbackContext { + private final Object obj; + private final ObjectStreamClass desc; + /** + * Thread this context is in use by. + * As this only works in one thread, we do not need to worry about thread-safety. + */ + private Thread thread; + + public SerialCallbackContext(Object obj, ObjectStreamClass desc) { + this.obj = obj; + this.desc = desc; + this.thread = Thread.currentThread(); + } + + public Object getObj() throws NotActiveException { + checkAndSetUsed(); + return obj; + } + + public ObjectStreamClass getDesc() { + return desc; + } + + private void checkAndSetUsed() throws NotActiveException { + if (thread != Thread.currentThread()) { + throw new NotActiveException( + "not in readObject invocation or fields already read"); + } + thread = null; + } + + public void setUsed() { + thread = null; + } + } + +
--- a/jdk/src/share/classes/java/lang/AbstractStringBuilder.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/lang/AbstractStringBuilder.java Tue Nov 09 22:53:18 2010 -0800 @@ -100,7 +100,8 @@ * @param minimumCapacity the minimum desired capacity. */ public void ensureCapacity(int minimumCapacity) { - ensureCapacityInternal(minimumCapacity); + if (minimumCapacity > 0) + ensureCapacityInternal(minimumCapacity); } /** @@ -108,6 +109,7 @@ * never synchronized. */ private void ensureCapacityInternal(int minimumCapacity) { + // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); }
--- a/jdk/src/share/classes/java/lang/ClassLoader.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/lang/ClassLoader.java Tue Nov 09 22:53:18 2010 -0800 @@ -553,6 +553,13 @@ * If either <tt>off</tt> or <tt>len</tt> is negative, or if * <tt>off+len</tt> is greater than <tt>b.length</tt>. * + * @throws SecurityException + * If an attempt is made to add this class to a package that + * contains classes that were signed by a different set of + * certificates than this class, or if an attempt is made + * to define a class in a package with a fully-qualified name + * that starts with "{@code java.}". + * * @see #loadClass(String, boolean) * @see #resolveClass(Class) *
--- a/jdk/src/share/classes/java/net/AbstractPlainSocketImpl.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/net/AbstractPlainSocketImpl.java Tue Nov 09 22:53:18 2010 -0800 @@ -28,9 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.InterruptedIOException; import java.io.FileDescriptor; -import java.io.ByteArrayOutputStream; import sun.net.ConnectionResetException; import sun.net.NetHooks; @@ -58,7 +56,7 @@ protected int fdUseCount = 0; /* lock when increment/decrementing fdUseCount */ - protected Object fdLock = new Object(); + protected final Object fdLock = new Object(); /* indicates a close is pending on the file descriptor */ protected boolean closePending = false; @@ -68,7 +66,7 @@ private int CONNECTION_RESET_PENDING = 1; private int CONNECTION_RESET = 2; private int resetState; - private Object resetLock = new Object(); + private final Object resetLock = new Object(); /** * Load net library into runtime. @@ -100,25 +98,24 @@ protected void connect(String host, int port) throws UnknownHostException, IOException { - IOException pending = null; + boolean connected = false; try { InetAddress address = InetAddress.getByName(host); this.port = port; this.address = address; - try { - connectToAddress(address, port, timeout); - return; - } catch (IOException e) { - pending = e; + connectToAddress(address, port, timeout); + connected = true; + } finally { + if (!connected) { + try { + close(); + } catch (IOException ioe) { + /* Do nothing. If connect threw an exception then + it will be passed up the call stack */ + } } - } catch (UnknownHostException e) { - pending = e; } - - // everything failed - close(); - throw pending; } /** @@ -151,22 +148,29 @@ * SocketAddress subclass not supported by this socket * @since 1.4 */ - protected void connect(SocketAddress address, int timeout) throws IOException { - if (address == null || !(address instanceof InetSocketAddress)) - throw new IllegalArgumentException("unsupported address type"); - InetSocketAddress addr = (InetSocketAddress) address; - if (addr.isUnresolved()) - throw new UnknownHostException(addr.getHostName()); - this.port = addr.getPort(); - this.address = addr.getAddress(); + protected void connect(SocketAddress address, int timeout) + throws IOException { + boolean connected = false; + try { + if (address == null || !(address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + InetSocketAddress addr = (InetSocketAddress) address; + if (addr.isUnresolved()) + throw new UnknownHostException(addr.getHostName()); + this.port = addr.getPort(); + this.address = addr.getAddress(); - try { connectToAddress(this.address, port, timeout); - return; - } catch (IOException e) { - // everything failed - close(); - throw e; + connected = true; + } finally { + if (!connected) { + try { + close(); + } catch (IOException ioe) { + /* Do nothing. If connect threw an exception then + it will be passed up the call stack */ + } + } } } @@ -311,7 +315,7 @@ } } try { - FileDescriptor fd = acquireFD(); + acquireFD(); try { socketConnect(address, port, timeout); /* socket may have been closed during poll/select */ @@ -370,7 +374,7 @@ * @param s the connection */ protected void accept(SocketImpl s) throws IOException { - FileDescriptor fd = acquireFD(); + acquireFD(); try { socketAccept(s); } finally { @@ -562,7 +566,6 @@ close(); } - /* * "Acquires" and returns the FileDescriptor for this impl *
--- a/jdk/src/share/classes/java/nio/Direct-X-Buffer.java.template Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/nio/Direct-X-Buffer.java.template Tue Nov 09 22:53:18 2010 -0800 @@ -27,6 +27,7 @@ package java.nio; +import java.io.FileDescriptor; import sun.misc.Cleaner; import sun.misc.Unsafe; import sun.misc.VM; @@ -114,7 +115,7 @@ // Direct$Type$Buffer$RW$(int cap) { // package-private #if[rw] - super(-1, 0, cap, cap, false); + super(-1, 0, cap, cap); boolean pa = VM.isDirectMemoryPageAligned(); int ps = Bits.pageSize(); long size = Math.max(1L, (long)cap + (pa ? ps : 0)); @@ -145,7 +146,7 @@ // Invoked only by JNI: NewDirectByteBuffer(void*, long) // private Direct$Type$Buffer(long addr, int cap) { - super(-1, 0, cap, cap, false); + super(-1, 0, cap, cap); address = addr; cleaner = null; } @@ -154,14 +155,17 @@ // For memory-mapped buffers -- invoked by FileChannelImpl via reflection // - protected Direct$Type$Buffer$RW$(int cap, long addr, Runnable unmapper) { + protected Direct$Type$Buffer$RW$(int cap, long addr, + FileDescriptor fd, + Runnable unmapper) + { #if[rw] - super(-1, 0, cap, cap, true); + super(-1, 0, cap, cap, fd); address = addr; viewedBuffer = null; cleaner = Cleaner.create(this, unmapper); #else[rw] - super(cap, addr, unmapper); + super(cap, addr, fd, unmapper); #end[rw] }
--- a/jdk/src/share/classes/java/nio/MappedByteBuffer.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/nio/MappedByteBuffer.java Tue Nov 09 22:53:18 2010 -0800 @@ -25,6 +25,7 @@ package java.nio; +import java.io.FileDescriptor; import sun.misc.Unsafe; @@ -71,26 +72,26 @@ // for optimization purposes, it's easier to do it the other way around. // This works because DirectByteBuffer is a package-private class. - // Volatile to make sure that the finalization thread sees the current - // value of this so that a region is not accidentally unmapped again later. - volatile boolean isAMappedBuffer; // package-private + // For mapped buffers, a FileDescriptor that may be used for mapping + // operations if valid; null if the buffer is not mapped. + private final FileDescriptor fd; // This should only be invoked by the DirectByteBuffer constructors // MappedByteBuffer(int mark, int pos, int lim, int cap, // package-private - boolean mapped) + FileDescriptor fd) { super(mark, pos, lim, cap); - isAMappedBuffer = mapped; + this.fd = fd; } MappedByteBuffer(int mark, int pos, int lim, int cap) { // package-private super(mark, pos, lim, cap); - isAMappedBuffer = false; + this.fd = null; } private void checkMapped() { - if (!isAMappedBuffer) + if (fd == null) // Can only happen if a luser explicitly casts a direct byte buffer throw new UnsupportedOperationException(); } @@ -191,13 +192,12 @@ checkMapped(); if ((address != 0) && (capacity() != 0)) { long offset = mappingOffset(); - force0(mappingAddress(offset), mappingLength(offset)); + force0(fd, mappingAddress(offset), mappingLength(offset)); } return this; } private native boolean isLoaded0(long address, long length, int pageCount); private native void load0(long address, long length); - private native void force0(long address, long length); - + private native void force0(FileDescriptor fd, long address, long length); }
--- a/jdk/src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java Tue Nov 09 11:45:00 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,572 +0,0 @@ -/* - * Copyright (c) 2007, 2009, Oracle and/or its affiliates. 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.nio.channels; - -import java.nio.channels.spi.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.Future; -import java.io.IOException; -import java.net.SocketOption; -import java.net.SocketAddress; -import java.net.ProtocolFamily; -import java.nio.ByteBuffer; - -/** - * An asynchronous channel for datagram-oriented sockets. - * - * <p> An asynchronous datagram channel is created by invoking one of the {@link - * #open open} methods defined by this class. It is not possible to create a channel - * for an arbitrary, pre-existing datagram socket. A newly-created asynchronous - * datagram channel is open but not connected. It need not be connected in order - * for the {@link #send send} and {@link #receive receive} methods to be used. - * A datagram channel may be connected, by invoking its {@link #connect connect} - * method, in order to avoid the overhead of the security checks that are otherwise - * performed as part of every send and receive operation when a security manager - * is set. The channel must be connected in order to use the {@link #read read} - * and {@link #write write} methods, since those methods do not accept or return - * socket addresses. Once connected, an asynchronous datagram channel remains - * connected until it is disconnected or closed. - * - * <p> Socket options are configured using the {@link #setOption(SocketOption,Object) - * setOption} method. An asynchronous datagram channel to an Internet Protocol - * (IP) socket supports the following options: - * <blockquote> - * <table border> - * <tr> - * <th>Option Name</th> - * <th>Description</th> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} </td> - * <td> The size of the socket send buffer </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} </td> - * <td> The size of the socket receive buffer </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} </td> - * <td> Re-use address </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#SO_BROADCAST SO_BROADCAST} </td> - * <td> Allow transmission of broadcast datagrams </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#IP_TOS IP_TOS} </td> - * <td> The Type of Service (ToS) octet in the Internet Protocol (IP) header </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#IP_MULTICAST_IF IP_MULTICAST_IF} </td> - * <td> The network interface for Internet Protocol (IP) multicast datagrams </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#IP_MULTICAST_TTL - * IP_MULTICAST_TTL} </td> - * <td> The <em>time-to-live</em> for Internet Protocol (IP) multicast - * datagrams </td> - * </tr> - * <tr> - * <td> {@link java.net.StandardSocketOption#IP_MULTICAST_LOOP - * IP_MULTICAST_LOOP} </td> - * <td> Loopback for Internet Protocol (IP) multicast datagrams </td> - * </tr> - * </table> - * </blockquote> - * Additional (implementation specific) options may also be supported. - * - * <p> Asynchronous datagram channels allow more than one read/receive and - * write/send to be oustanding at any given time. - * - * <p> <b>Usage Example:</b> - * <pre> - * final AsynchronousDatagramChannel dc = AsynchronousDatagramChannel.open() - * .bind(new InetSocketAddress(4000)); - * - * // print the source address of all packets that we receive - * dc.receive(buffer, buffer, new CompletionHandler<SocketAddress,ByteBuffer>() { - * public void completed(SocketAddress sa, ByteBuffer buffer) { - * System.out.println(sa); - * buffer.clear(); - * dc.receive(buffer, buffer, this); - * } - * public void failed(Throwable exc, ByteBuffer buffer) { - * ... - * } - * }); - * </pre> - * - * @since 1.7 - */ - -public abstract class AsynchronousDatagramChannel - implements AsynchronousByteChannel, MulticastChannel -{ - private final AsynchronousChannelProvider provider; - - /** - * Initializes a new instance of this class. - */ - protected AsynchronousDatagramChannel(AsynchronousChannelProvider provider) { - this.provider = provider; - } - - /** - * Returns the provider that created this channel. - */ - public final AsynchronousChannelProvider provider() { - return provider; - } - - /** - * Opens an asynchronous datagram channel. - * - * <p> The new channel is created by invoking the {@link - * java.nio.channels.spi.AsynchronousChannelProvider#openAsynchronousDatagramChannel - * openAsynchronousDatagramChannel} method on the {@link - * java.nio.channels.spi.AsynchronousChannelProvider} object that created - * the given group (or the default provider where {@code group} is {@code - * null}). - * - * <p> The {@code family} parameter is used to specify the {@link ProtocolFamily}. - * If the datagram channel is to be used for Internet Protocol {@link - * MulticastChannel multicasting} then this parameter should correspond to - * the address type of the multicast groups that this channel will join. - * - * @param family - * The protocol family, or {@code null} to use the default protocol - * family - * @param group - * The group to which the newly constructed channel should be bound, - * or {@code null} for the default group - * - * @return A new asynchronous datagram channel - * - * @throws UnsupportedOperationException - * If the specified protocol family is not supported. For example, - * suppose the parameter is specified as {@link - * java.net.StandardProtocolFamily#INET6 INET6} but IPv6 is not - * enabled on the platform. - * @throws ShutdownChannelGroupException - * The specified group is shutdown - * @throws IOException - * If an I/O error occurs - */ - public static AsynchronousDatagramChannel open(ProtocolFamily family, - AsynchronousChannelGroup group) - throws IOException - { - AsynchronousChannelProvider provider = (group == null) ? - AsynchronousChannelProvider.provider() : group.provider(); - return provider.openAsynchronousDatagramChannel(family, group); - } - - /** - * Opens an asynchronous datagram channel. - * - * <p> This method returns an asynchronous datagram channel that is - * bound to the <em>default group</em>. This method is equivalent to evaluating - * the expression: - * <blockquote><pre> - * open((ProtocolFamily)null, (AsynchronousChannelGroup)null); - * </pre></blockquote> - * - * @return A new asynchronous datagram channel - * - * @throws IOException - * If an I/O error occurs - */ - public static AsynchronousDatagramChannel open() - throws IOException - { - return open(null, null); - } - - // -- Socket-specific operations -- - - /** - * @throws AlreadyBoundException {@inheritDoc} - * @throws UnsupportedAddressTypeException {@inheritDoc} - * @throws ClosedChannelException {@inheritDoc} - * @throws IOException {@inheritDoc} - * @throws SecurityException - * If a security manager has been installed and its {@link - * SecurityManager#checkListen checkListen} method denies the - * operation - */ - @Override - public abstract AsynchronousDatagramChannel bind(SocketAddress local) - throws IOException; - - /** - * @throws IllegalArgumentException {@inheritDoc} - * @throws ClosedChannelException {@inheritDoc} - * @throws IOException {@inheritDoc} - */ - @Override - public abstract <T> AsynchronousDatagramChannel setOption(SocketOption<T> name, T value) - throws IOException; - - /** - * Returns the remote address to which this channel is connected. - * - * <p> Where the channel is connected to an Internet Protocol socket address - * then the return value from this method is of type {@link - * java.net.InetSocketAddress}. - * - * @return The remote address; {@code null} if the channel's socket is not - * connected - * - * @throws ClosedChannelException - * If the channel is closed - * @throws IOException - * If an I/O error occurs - */ - public abstract SocketAddress getRemoteAddress() throws IOException; - - /** - * Connects this channel's socket. - * - * <p> The channel's socket is configured so that it only receives - * datagrams from, and sends datagrams to, the given remote <i>peer</i> - * address. Once connected, datagrams may not be received from or sent to - * any other address. A datagram socket remains connected until it is - * explicitly disconnected or until it is closed. - * - * <p> This method performs exactly the same security checks as the {@link - * java.net.DatagramSocket#connect connect} method of the {@link - * java.net.DatagramSocket} class. That is, if a security manager has been - * installed then this method verifies that its {@link - * java.lang.SecurityManager#checkAccept checkAccept} and {@link - * java.lang.SecurityManager#checkConnect checkConnect} methods permit - * datagrams to be received from and sent to, respectively, the given - * remote address. - * - * <p> This method may be invoked at any time. Whether it has any effect - * on outstanding read or write operations is implementation specific and - * therefore not specified. - * - * @param remote - * The remote address to which this channel is to be connected - * - * @return This datagram channel - * - * @throws ClosedChannelException - * If this channel is closed - * - * @throws SecurityException - * If a security manager has been installed - * and it does not permit access to the given remote address - * - * @throws IOException - * If some other I/O error occurs - */ - public abstract AsynchronousDatagramChannel connect(SocketAddress remote) - throws IOException; - - /** - * Disconnects this channel's socket. - * - * <p> The channel's socket is configured so that it can receive datagrams - * from, and sends datagrams to, any remote address so long as the security - * manager, if installed, permits it. - * - * <p> This method may be invoked at any time. Whether it has any effect - * on outstanding read or write operations is implementation specific and - * therefore not specified. - * - * @return This datagram channel - * - * @throws IOException - * If some other I/O error occurs - */ - public abstract AsynchronousDatagramChannel disconnect() throws IOException; - - /** - * Receives a datagram via this channel. - * - * <p> This method initiates the receiving of a datagram into the given - * buffer. The {@code handler} parameter is a completion handler that is - * invoked when the receive operation completes (or fails). The result - * passed to the completion handler is the datagram's source address. - * - * <p> The datagram is transferred into the given byte buffer starting at - * its current position, as if by a regular {@link AsynchronousByteChannel#read - * read} operation. If there are fewer bytes remaining in the buffer - * than are required to hold the datagram then the remainder of the datagram - * is silently discarded. - * - * <p> If a timeout is specified and the timeout elapses before the operation - * completes then the operation completes with the exception {@link - * InterruptedByTimeoutException}. When a timeout elapses then the state of - * the {@link ByteBuffer} is not defined. The buffers should be discarded or - * at least care must be taken to ensure that the buffer is not accessed - * while the channel remains open. - * - * <p> When a security manager has been installed and the channel is not - * connected, then it verifies that the source's address and port number are - * permitted by the security manager's {@link SecurityManager#checkAccept - * checkAccept} method. The permission check is performed with privileges that - * are restricted by the calling context of this method. If the permission - * check fails then the operation completes with a {@link SecurityException}. - * The overhead of this security check can be avoided by first connecting the - * socket via the {@link #connect connect} method. - * - * @param dst - * The buffer into which the datagram is to be transferred - * @param timeout - * The timeout, or {@code 0L} for no timeout - * @param unit - * The time unit of the {@code timeout} argument - * @param attachment - * The object to attach to the I/O operation; can be {@code null} - * @param handler - * The handler for consuming the result - * - * @throws IllegalArgumentException - * If the timeout is negative or the buffer is read-only - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - public abstract <A> void receive(ByteBuffer dst, - long timeout, - TimeUnit unit, - A attachment, - CompletionHandler<SocketAddress,? super A> handler); - - /** - * Receives a datagram via this channel. - * - * <p> This method initiates the receiving of a datagram into the given - * buffer. The {@code handler} parameter is a completion handler that is - * invoked when the receive operation completes (or fails). The result - * passed to the completion handler is the datagram's source address. - * - * <p> This method is equivalent to invoking {@link - * #receive(ByteBuffer,long,TimeUnit,Object,CompletionHandler)} with a - * timeout of {@code 0L}. - * - * @param dst - * The buffer into which the datagram is to be transferred - * @param attachment - * The object to attach to the I/O operation; can be {@code null} - * @param handler - * The handler for consuming the result - * - * @throws IllegalArgumentException - * If the buffer is read-only - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - public final <A> void receive(ByteBuffer dst, - A attachment, - CompletionHandler<SocketAddress,? super A> handler) - { - receive(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler); - } - - /** - * Receives a datagram via this channel. - * - * <p> This method initiates the receiving of a datagram into the given - * buffer. The method behaves in exactly the same manner as the {@link - * #receive(ByteBuffer,Object,CompletionHandler) - * receive(ByteBuffer,Object,CompletionHandler)} method except that instead - * of specifying a completion handler, this method returns a {@code Future} - * representing the pending result. The {@code Future}'s {@link Future#get() - * get} method returns the datagram's source address. - * - * @param dst - * The buffer into which the datagram is to be transferred - * - * @return a {@code Future} object representing the pending result - * - * @throws IllegalArgumentException - * If the buffer is read-only - */ - public abstract Future<SocketAddress> receive(ByteBuffer dst); - - /** - * Sends a datagram via this channel. - * - * <p> This method initiates sending of a datagram from the given buffer to - * the given address. The {@code handler} parameter is a completion handler - * that is invoked when the send completes (or fails). The result passed to - * the completion handler is the number of bytes sent. - * - * <p> Otherwise this method works in the same manner as the {@link - * AsynchronousByteChannel#write(ByteBuffer,Object,CompletionHandler)} - * method. - * - * @param src - * The buffer containing the datagram to be sent - * @param target - * The address to which the datagram is to be sent - * @param attachment - * The object to attach to the I/O operation; can be {@code null} - * @param handler - * The handler for consuming the result - * - * @throws UnresolvedAddressException - * If the given remote address is not fully resolved - * @throws UnsupportedAddressTypeException - * If the type of the given remote address is not supported - * @throws IllegalArgumentException - * If the channel's socket is connected and is connected to an - * address that is not equal to {@code target} - * @throws SecurityException - * If a security manager has been installed and it does not permit - * datagrams to be sent to the given address - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - public abstract <A> void send(ByteBuffer src, - SocketAddress target, - A attachment, - CompletionHandler<Integer,? super A> handler); - - /** - * Sends a datagram via this channel. - * - * <p> This method initiates sending of a datagram from the given buffer to - * the given address. The method behaves in exactly the same manner as the - * {@link #send(ByteBuffer,SocketAddress,Object,CompletionHandler) - * send(ByteBuffer,SocketAddress,Object,CompletionHandler)} method except - * that instead of specifying a completion handler, this method returns a - * {@code Future} representing the pending result. The {@code Future}'s - * {@link Future#get() get} method returns the number of bytes sent. - * - * @param src - * The buffer containing the datagram to be sent - * @param target - * The address to which the datagram is to be sent - * - * @return a {@code Future} object representing the pending result - * - * @throws UnresolvedAddressException - * If the given remote address is not fully resolved - * @throws UnsupportedAddressTypeException - * If the type of the given remote address is not supported - * @throws IllegalArgumentException - * If the channel's socket is connected and is connected to an - * address that is not equal to {@code target} - * @throws SecurityException - * If a security manager has been installed and it does not permit - * datagrams to be sent to the given address - */ - public abstract Future<Integer> send(ByteBuffer src, SocketAddress target); - - /** - * Receives a datagram via this channel. - * - * <p> This method initiates the receiving of a datagram into the given - * buffer. The {@code handler} parameter is a completion handler that is - * invoked when the receive operation completes (or fails). The result - * passed to the completion handler is number of bytes read. - * - * <p> This method may only be invoked if this channel is connected, and it - * only accepts datagrams from the peer that the channel is connected too. - * The datagram is transferred into the given byte buffer starting at - * its current position and exactly as specified in the {@link - * AsynchronousByteChannel} interface. If there are fewer bytes - * remaining in the buffer than are required to hold the datagram then the - * remainder of the datagram is silently discarded. - * - * <p> If a timeout is specified and the timeout elapses before the operation - * completes then the operation completes with the exception {@link - * InterruptedByTimeoutException}. When a timeout elapses then the state of - * the {@link ByteBuffer} is not defined. The buffers should be discarded or - * at least care must be taken to ensure that the buffer is not accessed - * while the channel remains open. - * - * @param dst - * The buffer into which the datagram is to be transferred - * @param timeout - * The timeout, or {@code 0L} for no timeout - * @param unit - * The time unit of the {@code timeout} argument - * @param attachment - * The object to attach to the I/O operation; can be {@code null} - * @param handler - * The handler for consuming the result - * - * @throws IllegalArgumentException - * If the timeout is negative or buffer is read-only - * @throws NotYetConnectedException - * If this channel is not connected - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - public abstract <A> void read(ByteBuffer dst, - long timeout, - TimeUnit unit, - A attachment, - CompletionHandler<Integer,? super A> handler); - - /** - * @throws NotYetConnectedException - * If this channel is not connected - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - @Override - public final <A> void read(ByteBuffer dst, - A attachment, - CompletionHandler<Integer,? super A> handler) - { - read(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler); - } - - /** - * @throws NotYetConnectedException - * If this channel is not connected - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - @Override - public abstract Future<Integer> read(ByteBuffer dst); - - /** - * @throws NotYetConnectedException - * If this channel is not connected - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - @Override - public abstract <A> void write(ByteBuffer src, - A attachment, - CompletionHandler<Integer,? super A> handler); - - - /** - * @throws NotYetConnectedException - * If this channel is not connected - * @throws ShutdownChannelGroupException - * If the channel group has terminated - */ - @Override - public abstract Future<Integer> write(ByteBuffer src); -}
--- a/jdk/src/share/classes/java/nio/channels/package-info.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/nio/channels/package-info.java Tue Nov 09 22:53:18 2010 -0800 @@ -232,8 +232,6 @@ * <td>An asynchronous channel to a stream-oriented connecting socket</td></tr> * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousServerSocketChannel} </tt></td> * <td>An asynchronous channel to a stream-oriented listening socket</td></tr> - * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousDatagramChannel}</tt></td> - * <td>An asynchronous channel to a datagram-oriented socket</td></tr> * <tr><td valign=top><tt>{@link java.nio.channels.CompletionHandler}</tt></td> * <td>A handler for consuming the result of an asynchronous operation</td></tr> * <tr><td valign=top><tt>{@link java.nio.channels.AsynchronousChannelGroup}</tt></td>
--- a/jdk/src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java Tue Nov 09 22:53:18 2010 -0800 @@ -26,7 +26,6 @@ package java.nio.channels.spi; import java.nio.channels.*; -import java.net.ProtocolFamily; import java.io.IOException; import java.util.Iterator; import java.util.ServiceLoader; @@ -239,26 +238,4 @@ */ public abstract AsynchronousSocketChannel openAsynchronousSocketChannel (AsynchronousChannelGroup group) throws IOException; - - /** - * Opens an asynchronous datagram channel. - * - * @param family - * The protocol family, or {@code null} for the default protocol - * family - * @param group - * The group to which the channel is bound, or {@code null} to - * bind to the default group - * - * @return The new channel - * - * @throws IllegalChannelGroupException - * If the provider that created the group differs from this provider - * @throws ShutdownChannelGroupException - * The group is shutdown - * @throws IOException - * If an I/O error occurs - */ - public abstract AsynchronousDatagramChannel openAsynchronousDatagramChannel - (ProtocolFamily family, AsynchronousChannelGroup group) throws IOException; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/security/AlgorithmConstraints.java Tue Nov 09 22:53:18 2010 -0800 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.security; + +import java.util.Set; + +/** + * This interface specifies constraints for cryptographic algorithms, + * keys (key sizes), and other algorithm parameters. + * <p> + * {@code AlgorithmConstraints} objects are immutable. An implementation + * of this interface should not provide methods that can change the state + * of an instance once it has been created. + * <p> + * Note that {@code AlgorithmConstraints} can be used to represent the + * restrictions described by the security properties + * {@code jdk.certpath.disabledAlgorithms} and + * {@code jdk.tls.disabledAlgorithms}, or could be used by a + * concrete {@code PKIXCertPathChecker} to check whether a specified + * certificate in the certification path contains the required algorithm + * constraints. + * + * @see javax.net.ssl.SSLParameters#getAlgorithmConstraints + * @see javax.net.ssl.SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + * + * @since 1.7 + */ + +public interface AlgorithmConstraints { + + /** + * Determines whether an algorithm is granted permission for the + * specified cryptographic primitives. + * + * @param primitives a set of cryptographic primitives + * @param algorithm the algorithm name + * @param parameters the algorithm parameters, or null if no additional + * parameters + * + * @return true if the algorithm is permitted and can be used for all + * of the specified cryptographic primitives + * + * @throws IllegalArgumentException if primitives or algorithm is null + * or empty + */ + public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, AlgorithmParameters parameters); + + /** + * Determines whether a key is granted permission for the specified + * cryptographic primitives. + * <p> + * This method is usually used to check key size and key usage. + * + * @param primitives a set of cryptographic primitives + * @param key the key + * + * @return true if the key can be used for all of the specified + * cryptographic primitives + * + * @throws IllegalArgumentException if primitives is null or empty, + * or the key is null + */ + public boolean permits(Set<CryptoPrimitive> primitives, Key key); + + /** + * Determines whether an algorithm and the corresponding key are granted + * permission for the specified cryptographic primitives. + * + * @param primitives a set of cryptographic primitives + * @param algorithm the algorithm name + * @param key the key + * @param parameters the algorithm parameters, or null if no additional + * parameters + * + * @return true if the key and the algorithm can be used for all of the + * specified cryptographic primitives + * + * @throws IllegalArgumentException if primitives or algorithm is null + * or empty, or the key is null + */ + public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, Key key, AlgorithmParameters parameters); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/security/CryptoPrimitive.java Tue Nov 09 22:53:18 2010 -0800 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.security; + +/** + * An enumeration of cryptographic primitives. + * + * @since 1.7 + */ +public enum CryptoPrimitive { + /** + * Hash function + */ + MESSAGE_DIGEST, + + /** + * Cryptographic random number generator + */ + SECURE_RANDOM, + + /** + * Symmetric primitive: block cipher + */ + BLOCK_CIPHER, + + /** + * Symmetric primitive: stream cipher + */ + STREAM_CIPHER, + + /** + * Symmetric primitive: message authentication code + */ + MAC, + + /** + * Symmetric primitive: key wrap + */ + KEY_WRAP, + + /** + * Asymmetric primitive: public key encryption + */ + PUBLIC_KEY_ENCRYPTION, + + /** + * Asymmetric primitive: signature scheme + */ + SIGNATURE, + + /** + * Asymmetric primitive: key encapsulation mechanism + */ + KEY_ENCAPSULATION, + + /** + * Asymmetric primitive: key agreement and key distribution + */ + KEY_AGREEMENT +}
--- a/jdk/src/share/classes/java/text/DateFormatSymbols.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/text/DateFormatSymbols.java Tue Nov 09 22:53:18 2010 -0800 @@ -44,11 +44,12 @@ import java.lang.ref.SoftReference; import java.text.spi.DateFormatSymbolsProvider; import java.util.Arrays; -import java.util.Hashtable; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; import java.util.TimeZone; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.spi.LocaleServiceProvider; import sun.util.LocaleServiceProviderPool; import sun.util.TimeZoneNameUtility; @@ -321,20 +322,64 @@ * @since 1.6 */ public static final DateFormatSymbols getInstance(Locale locale) { + DateFormatSymbols dfs = getProviderInstance(locale); + if (dfs != null) { + return dfs; + } + return (DateFormatSymbols) getCachedInstance(locale).clone(); + } + + /** + * Returns a DateFormatSymbols provided by a provider or found in + * the cache. Note that this method returns a cached instance, + * not its clone. Therefore, the instance should never be given to + * an application. + */ + static final DateFormatSymbols getInstanceRef(Locale locale) { + DateFormatSymbols dfs = getProviderInstance(locale); + if (dfs != null) { + return dfs; + } + return getCachedInstance(locale); + } + + private static DateFormatSymbols getProviderInstance(Locale locale) { + DateFormatSymbols providersInstance = null; // Check whether a provider can provide an implementation that's closer // to the requested locale than what the Java runtime itself can provide. LocaleServiceProviderPool pool = LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class); if (pool.hasProviders()) { - DateFormatSymbols providersInstance = pool.getLocalizedObject( - DateFormatSymbolsGetter.INSTANCE, locale); - if (providersInstance != null) { - return providersInstance; + providersInstance = pool.getLocalizedObject( + DateFormatSymbolsGetter.INSTANCE, locale); + } + return providersInstance; + } + + /** + * Returns a cached DateFormatSymbols if it's found in the + * cache. Otherwise, this method returns a newly cached instance + * for the given locale. + */ + private static DateFormatSymbols getCachedInstance(Locale locale) { + SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale); + DateFormatSymbols dfs = null; + if (ref == null || (dfs = ref.get()) == null) { + dfs = new DateFormatSymbols(locale); + ref = new SoftReference<DateFormatSymbols>(dfs); + SoftReference<DateFormatSymbols> x = cachedInstances.putIfAbsent(locale, ref); + if (x != null) { + DateFormatSymbols y = x.get(); + if (y != null) { + dfs = y; + } else { + // Replace the empty SoftReference with ref. + cachedInstances.put(locale, ref); + } } } - - return new DateFormatSymbols(locale); + return dfs; } /** @@ -597,56 +642,44 @@ static final int millisPerHour = 60*60*1000; /** - * Cache to hold the FormatData and TimeZoneNames ResourceBundles - * of a Locale. + * Cache to hold DateFormatSymbols instances per Locale. */ - private static Hashtable cachedLocaleData = new Hashtable(3); - - /** - * Look up resource data for the desiredLocale in the cache; update the - * cache if necessary. - */ - private static ResourceBundle cacheLookup(Locale desiredLocale) { - ResourceBundle rb; - SoftReference data - = (SoftReference)cachedLocaleData.get(desiredLocale); - if (data == null) { - rb = LocaleData.getDateFormatData(desiredLocale); - data = new SoftReference(rb); - cachedLocaleData.put(desiredLocale, data); - } else { - if ((rb = (ResourceBundle)data.get()) == null) { - rb = LocaleData.getDateFormatData(desiredLocale); - data = new SoftReference(rb); - } - } - return rb; - } + private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances + = new ConcurrentHashMap<Locale, SoftReference<DateFormatSymbols>>(3); private void initializeData(Locale desiredLocale) { - int i; - ResourceBundle resource = cacheLookup(desiredLocale); + locale = desiredLocale; - // FIXME: cache only ResourceBundle. Hence every time, will do - // getObject(). This won't be necessary if the Resource itself - // is cached. - eras = (String[])resource.getObject("Eras"); + // Copy values of a cached instance if any. + SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale); + DateFormatSymbols dfs; + if (ref != null && (dfs = ref.get()) != null) { + copyMembers(dfs, this); + return; + } + + // Initialize the fields from the ResourceBundle for locale. + ResourceBundle resource = LocaleData.getDateFormatData(locale); + + eras = resource.getStringArray("Eras"); months = resource.getStringArray("MonthNames"); shortMonths = resource.getStringArray("MonthAbbreviations"); - String[] lWeekdays = resource.getStringArray("DayNames"); - weekdays = new String[8]; - weekdays[0] = ""; // 1-based - for (i=0; i<lWeekdays.length; i++) - weekdays[i+1] = lWeekdays[i]; - String[] sWeekdays = resource.getStringArray("DayAbbreviations"); - shortWeekdays = new String[8]; - shortWeekdays[0] = ""; // 1-based - for (i=0; i<sWeekdays.length; i++) - shortWeekdays[i+1] = sWeekdays[i]; ampms = resource.getStringArray("AmPmMarkers"); localPatternChars = resource.getString("DateTimePatternChars"); - locale = desiredLocale; + // Day of week names are stored in a 1-based array. + weekdays = toOneBasedArray(resource.getStringArray("DayNames")); + shortWeekdays = toOneBasedArray(resource.getStringArray("DayAbbreviations")); + } + + private static String[] toOneBasedArray(String[] src) { + int len = src.length; + String[] dst = new String[len + 1]; + dst[0] = ""; + for (int i = 0; i < len; i++) { + dst[i + 1] = src[i]; + } + return dst; } /**
--- a/jdk/src/share/classes/java/text/DecimalFormat.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/text/DecimalFormat.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. 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,9 +46,10 @@ import java.math.RoundingMode; import java.util.ArrayList; import java.util.Currency; -import java.util.Hashtable; import java.util.Locale; import java.util.ResourceBundle; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import sun.util.resources.LocaleData; @@ -394,14 +395,14 @@ public DecimalFormat() { Locale def = Locale.getDefault(Locale.Category.FORMAT); // try to get the pattern from the cache - String pattern = (String) cachedLocaleData.get(def); + String pattern = cachedLocaleData.get(def); if (pattern == null) { /* cache miss */ // Get the pattern for the default locale. ResourceBundle rb = LocaleData.getNumberFormatData(def); String[] all = rb.getStringArray("NumberPatterns"); pattern = all[0]; /* update cache */ - cachedLocaleData.put(def, pattern); + cachedLocaleData.putIfAbsent(def, pattern); } // Always applyPattern after the symbols are set @@ -3272,5 +3273,6 @@ /** * Cache to hold the NumberPattern of a Locale. */ - private static Hashtable cachedLocaleData = new Hashtable(3); + private static final ConcurrentMap<Locale, String> cachedLocaleData + = new ConcurrentHashMap<Locale, String>(3); }
--- a/jdk/src/share/classes/java/text/SimpleDateFormat.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/text/SimpleDateFormat.java Tue Nov 09 22:53:18 2010 -0800 @@ -44,13 +44,14 @@ import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; -import java.util.Hashtable; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.SimpleTimeZone; import java.util.TimeZone; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import sun.util.calendar.CalendarUtils; import sun.util.calendar.ZoneInfoFile; import sun.util.resources.LocaleData; @@ -503,14 +504,14 @@ /** * Cache to hold the DateTimePatterns of a Locale. */ - private static Hashtable<String,String[]> cachedLocaleData - = new Hashtable<String,String[]>(3); + private static final ConcurrentMap<String, String[]> cachedLocaleData + = new ConcurrentHashMap<String, String[]>(3); /** * Cache NumberFormat instances with Locale key. */ - private static Hashtable<Locale,NumberFormat> cachedNumberFormatData - = new Hashtable<Locale,NumberFormat>(3); + private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData + = new ConcurrentHashMap<Locale, NumberFormat>(3); /** * The Locale used to instantiate this @@ -579,7 +580,7 @@ initializeCalendar(locale); this.pattern = pattern; - this.formatData = DateFormatSymbols.getInstance(locale); + this.formatData = DateFormatSymbols.getInstanceRef(locale); this.locale = locale; initialize(locale); } @@ -632,9 +633,9 @@ dateTimePatterns = r.getStringArray("DateTimePatterns"); } /* update cache */ - cachedLocaleData.put(key, dateTimePatterns); + cachedLocaleData.putIfAbsent(key, dateTimePatterns); } - formatData = DateFormatSymbols.getInstance(loc); + formatData = DateFormatSymbols.getInstanceRef(loc); if ((timeStyle >= 0) && (dateStyle >= 0)) { Object[] dateTimeArgs = {dateTimePatterns[timeStyle], dateTimePatterns[dateStyle + 4]}; @@ -665,7 +666,7 @@ numberFormat.setGroupingUsed(false); /* update cache */ - cachedNumberFormatData.put(loc, numberFormat); + cachedNumberFormatData.putIfAbsent(loc, numberFormat); } numberFormat = (NumberFormat) numberFormat.clone(); @@ -897,7 +898,7 @@ * so we can call it from readObject(). */ private void initializeDefaultCentury() { - calendar.setTime( new Date() ); + calendar.setTimeInMillis(System.currentTimeMillis()); calendar.add( Calendar.YEAR, -80 ); parseAmbiguousDatesAsAfter(calendar.getTime()); } @@ -921,7 +922,7 @@ * @since 1.2 */ public void set2DigitYearStart(Date startDate) { - parseAmbiguousDatesAsAfter(startDate); + parseAmbiguousDatesAsAfter(new Date(startDate.getTime())); } /** @@ -934,7 +935,7 @@ * @since 1.2 */ public Date get2DigitYearStart() { - return defaultCenturyStart; + return (Date) defaultCenturyStart.clone(); } /**
--- a/jdk/src/share/classes/java/util/ArrayList.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/util/ArrayList.java Tue Nov 09 22:53:18 2010 -0800 @@ -176,6 +176,11 @@ * @param minCapacity the desired minimum capacity */ public void ensureCapacity(int minCapacity) { + if (minCapacity > 0) + ensureCapacityInternal(minCapacity); + } + + private void ensureCapacityInternal(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) @@ -403,7 +408,7 @@ * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boolean add(E e) { - ensureCapacity(size + 1); // Increments modCount!! + ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } @@ -420,7 +425,7 @@ public void add(int index, E element) { rangeCheckForAdd(index); - ensureCapacity(size + 1); // Increments modCount!! + ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; @@ -524,7 +529,7 @@ public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; - ensureCapacity(size + numNew); // Increments modCount + ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; @@ -550,7 +555,7 @@ Object[] a = c.toArray(); int numNew = a.length; - ensureCapacity(size + numNew); // Increments modCount + ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0)
--- a/jdk/src/share/classes/java/util/Calendar.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/util/Calendar.java Tue Nov 09 22:53:18 2010 -0800 @@ -51,6 +51,8 @@ import java.security.ProtectionDomain; import java.text.DateFormat; import java.text.DateFormatSymbols; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import sun.util.BuddhistCalendar; import sun.util.calendar.ZoneInfo; import sun.util.resources.LocaleData; @@ -837,7 +839,8 @@ * Cache to hold the firstDayOfWeek and minimalDaysInFirstWeek * of a Locale. */ - private static Hashtable<Locale, int[]> cachedLocaleData = new Hashtable<Locale, int[]>(3); + private static final ConcurrentMap<Locale, int[]> cachedLocaleData + = new ConcurrentHashMap<Locale, int[]>(3); // Special values of stamp[] /** @@ -1022,7 +1025,7 @@ // returns a BuddhistCalendar instance. if ("th".equals(aLocale.getLanguage()) && ("TH".equals(aLocale.getCountry()))) { - cal = new BuddhistCalendar(zone, aLocale); + cal = new BuddhistCalendar(zone, aLocale); } else { cal = new GregorianCalendar(zone, aLocale); } @@ -2588,7 +2591,7 @@ data = new int[2]; data[0] = Integer.parseInt(bundle.getString("firstDayOfWeek")); data[1] = Integer.parseInt(bundle.getString("minimalDaysInFirstWeek")); - cachedLocaleData.put(desiredLocale, data); + cachedLocaleData.putIfAbsent(desiredLocale, data); } firstDayOfWeek = data[0]; minimalDaysInFirstWeek = data[1];
--- a/jdk/src/share/classes/java/util/Locale.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/util/Locale.java Tue Nov 09 22:53:18 2010 -0800 @@ -758,7 +758,7 @@ } private static void initDefault() { - String language, region, country, variant; + String language, region, script, country, variant; language = AccessController.doPrivileged( new GetPropertyAction("user.language", "en")); // for compatibility, check for old user.region property @@ -774,43 +774,41 @@ country = region; variant = ""; } + script = ""; } else { + script = AccessController.doPrivileged( + new GetPropertyAction("user.script", "")); country = AccessController.doPrivileged( new GetPropertyAction("user.country", "")); variant = AccessController.doPrivileged( new GetPropertyAction("user.variant", "")); } - defaultLocale = getInstance(language, country, variant); + defaultLocale = getInstance(language, script, country, variant, null); } private static void initDefault(Locale.Category category) { - String language, region, country, variant; + // make sure defaultLocale is initialized + if (defaultLocale == null) { + initDefault(); + } + + Locale defaultCategoryLocale = getInstance( + AccessController.doPrivileged( + new GetPropertyAction(category.languageKey, defaultLocale.getLanguage())), + AccessController.doPrivileged( + new GetPropertyAction(category.scriptKey, defaultLocale.getScript())), + AccessController.doPrivileged( + new GetPropertyAction(category.countryKey, defaultLocale.getCountry())), + AccessController.doPrivileged( + new GetPropertyAction(category.variantKey, defaultLocale.getVariant())), + null); + switch (category) { case DISPLAY: - language = AccessController.doPrivileged( - new GetPropertyAction("user.language.display", "")); - if ("".equals(language)) { - defaultDisplayLocale = getDefault(); - } else { - country = AccessController.doPrivileged( - new GetPropertyAction("user.country.display", "")); - variant = AccessController.doPrivileged( - new GetPropertyAction("user.variant.display", "")); - defaultDisplayLocale = getInstance(language, country, variant); - } + defaultDisplayLocale = defaultCategoryLocale; break; case FORMAT: - language = AccessController.doPrivileged( - new GetPropertyAction("user.language.format", "")); - if ("".equals(language)) { - defaultFormatLocale = getDefault(); - } else { - country = AccessController.doPrivileged( - new GetPropertyAction("user.country.format", "")); - variant = AccessController.doPrivileged( - new GetPropertyAction("user.variant.format", "")); - defaultFormatLocale = getInstance(language, country, variant); - } + defaultFormatLocale = defaultCategoryLocale; break; } } @@ -2124,13 +2122,31 @@ * Category used to represent the default locale for * displaying user interfaces. */ - DISPLAY, + DISPLAY("user.language.display", + "user.script.display", + "user.country.display", + "user.variant.display"), /** * Category used to represent the default locale for * formatting dates, numbers, and/or currencies. */ - FORMAT, + FORMAT("user.language.format", + "user.script.format", + "user.country.format", + "user.variant.format"); + + Category(String languageKey, String scriptKey, String countryKey, String variantKey) { + this.languageKey = languageKey; + this.scriptKey = scriptKey; + this.countryKey = countryKey; + this.variantKey = variantKey; + } + + final String languageKey; + final String scriptKey; + final String countryKey; + final String variantKey; } /**
--- a/jdk/src/share/classes/java/util/TimeZone.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/util/TimeZone.java Tue Nov 09 22:53:18 2010 -0800 @@ -160,11 +160,6 @@ private static final int ONE_HOUR = 60*ONE_MINUTE; private static final int ONE_DAY = 24*ONE_HOUR; - /** - * Cache to hold the SimpleDateFormat objects for a Locale. - */ - private static Hashtable cachedLocaleData = new Hashtable(3); - // Proclaim serialization compatibility with JDK 1.1 static final long serialVersionUID = 3581463369166924961L;
--- a/jdk/src/share/classes/java/util/Vector.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/util/Vector.java Tue Nov 09 22:53:18 2010 -0800 @@ -222,8 +222,10 @@ * @param minCapacity the desired minimum capacity */ public synchronized void ensureCapacity(int minCapacity) { - modCount++; - ensureCapacityHelper(minCapacity); + if (minCapacity > 0) { + modCount++; + ensureCapacityHelper(minCapacity); + } } /**
--- a/jdk/src/share/classes/java/util/logging/LogRecord.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/java/util/logging/LogRecord.java Tue Nov 09 22:53:18 2010 -0800 @@ -529,8 +529,6 @@ Throwable throwable = new Throwable(); int depth = access.getStackTraceDepth(throwable); - String logClassName = "java.util.logging.Logger"; - String plogClassName = "sun.util.logging.PlatformLogger"; boolean lookingForLogger = true; for (int ix = 0; ix < depth; ix++) { // Calling getStackTraceElement directly prevents the VM @@ -538,13 +536,14 @@ StackTraceElement frame = access.getStackTraceElement(throwable, ix); String cname = frame.getClassName(); + boolean isLoggerImpl = isLoggerImplFrame(cname); if (lookingForLogger) { // Skip all frames until we have found the first logger frame. - if (cname.equals(logClassName) || cname.startsWith(plogClassName)) { + if (isLoggerImpl) { lookingForLogger = false; } } else { - if (!cname.equals(logClassName) && !cname.startsWith(plogClassName)) { + if (!isLoggerImpl) { // skip reflection call if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) { // We've found the relevant frame. @@ -558,4 +557,11 @@ // We haven't found a suitable frame, so just punt. This is // OK as we are only committed to making a "best effort" here. } + + private boolean isLoggerImplFrame(String cname) { + // the log record could be created for a platform logger + return (cname.equals("java.util.logging.Logger") || + cname.startsWith("java.util.logging.LoggingProxyImpl") || + cname.startsWith("sun.util.logging.")); + } }
--- a/jdk/src/share/classes/javax/management/ObjectName.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/management/ObjectName.java Tue Nov 09 22:53:18 2010 -0800 @@ -211,8 +211,8 @@ * reverse DNS name of the organization that specifies the MBeans, * followed by a period and a string whose interpretation is * determined by that organization. For example, MBeans specified by - * Sun Microsystems Inc., DNS name <code>sun.com</code>, would have - * domains such as <code>com.sun.MyDomain</code>. This is essentially + * <code>example.com</code> would have + * domains such as <code>com.example.MyDomain</code>. This is essentially * the same convention as for Java-language package names.</p> * * <p>The <b>serialVersionUID</b> of this class is <code>1081892073854801359L</code>.
--- a/jdk/src/share/classes/javax/management/build.xml Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/management/build.xml Tue Nov 09 22:53:18 2010 -0800 @@ -35,18 +35,18 @@ . Please also read the important comment on basedir definition below. --> -<project name="JMX API Version 1.4 - Java SE 6 implementation" - default="jar" - basedir="../../../../.." +<project name="JMX API Version 1.4 - Java SE 6 implementation" + default="jar" + basedir="../../../../.." > - <!-- IMPORTANT: BASEDIR DEFINITION - This file is assumed to be in: - <src_bundle_dir>/j2se/src/share/classes/javax/management - Thus the basedir for this project is defined above as: - basedir="../../../../.." + <!-- IMPORTANT: BASEDIR DEFINITION + This file is assumed to be in: + <src_bundle_dir>/j2se/src/share/classes/javax/management + Thus the basedir for this project is defined above as: + basedir="../../../../.." in order to be the parent dir of src subdir. - Result of the build will be placed in ${basedir}/build_jmx + Result of the build will be placed in ${basedir}/build_jmx as defined by ${dir.build} property below. --> @@ -163,14 +163,14 @@ <echo message=" BUILD_DATE = ${BUILD_DATE}" /> </target> - + <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- Call classes subtargets and rmic stubs generation --> - <target name="classes" depends="init,classes-javac,classes-rmic" - description="Call classes subtargets and rmic stubs generation" + <target name="classes" depends="init,classes-javac,classes-rmic" + description="Call classes subtargets and rmic stubs generation" /> @@ -183,13 +183,13 @@ <mkdir dir="${dir.build.classes}" /> <javac srcdir="${dir.src}" - destdir="${dir.build.classes}" + destdir="${dir.build.classes}" source="${flag.javac.source}" debug="${flag.debug}" debuglevel="${flag.debug.level}" optimize="${flag.optimize}" includeAntRuntime="no" - includeJavaRuntime="no" + includeJavaRuntime="no" > <include name="javax/management/**"/> <include name="com/sun/jmx/**"/> @@ -253,7 +253,7 @@ <mkdir dir="${dir.build.lib}" /> - <jar jarfile="${dir.build.lib}/jmx.jar" + <jar jarfile="${dir.build.lib}/jmx.jar" update="true" > @@ -269,10 +269,10 @@ <attribute name="Sealed" value="true" /> <attribute name="Specification-Title" value="JMX(TM) API" /> <attribute name="Specification-Version" value="1.4" /> - <attribute name="Specification-Vendor" value="Sun Microsystems, Inc." /> + <attribute name="Specification-Vendor" value="Oracle Corporation" /> <attribute name="Implementation-Title" value="JMX(TM) API, Java SE 6 implementation" /> - <attribute name="Implementation-Version" value="${BUILD_DATE} rebuild of Mustang JMX sources" /> - <attribute name="Implementation-Vendor" value="Source bundle from Sun Microsystems, Inc. - Customer rebuilt" /> + <attribute name="Implementation-Version" value="${BUILD_DATE} rebuild of Java SE JMX sources" /> + <attribute name="Implementation-Vendor" value="Source bundle from Oracle Corporation - Customer rebuilt" /> </section> </manifest> </jar> @@ -295,16 +295,16 @@ <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- Delete build directory and all its contents --> - <target name="clean-all" + <target name="clean-all" description="Delete build directory and all its contents" > <delete dir="${dir.build}" /> - </target> - - + </target> + + <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- Clean all and build everything --> - <target name="all" depends="clean-all,jar" + <target name="all" depends="clean-all,jar" description="Clean all and build everything" />
--- a/jdk/src/share/classes/javax/management/modelmbean/ModelMBeanNotificationInfo.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/management/modelmbean/ModelMBeanNotificationInfo.java Tue Nov 09 22:53:18 2010 -0800 @@ -92,8 +92,6 @@ * @since 1.5 */ -// Sun Microsystems, Sept. 2002: Revisited for JMX 1.2 (DF) -// @SuppressWarnings("serial") // serialVersionUID is not constant public class ModelMBeanNotificationInfo extends MBeanNotificationInfo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/javax/net/ssl/ExtendedSSLSession.java Tue Nov 09 22:53:18 2010 -0800 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.net.ssl; + +/** + * Extends the <code>SSLSession</code> interface to support additional + * session attributes. + * + * @since 1.7 + */ +public abstract class ExtendedSSLSession implements SSLSession { + /** + * Obtains an array of supported signature algorithms that the local side + * is willing to use. + * <p> + * Note: this method is used to indicate to the peer which signature + * algorithms may be used for digital signatures in TLS 1.2. It is + * not meaningful for TLS versions prior to 1.2. + * <p> + * The signature algorithm name must be a standard Java Security + * name (such as "SHA1withRSA", "SHA256withECDSA", and so on). + * See Appendix A in the <a href= + * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> + * Java Cryptography Architecture API Specification & Reference </a> + * for information about standard algorithm names. + * <p> + * Note: the local supported signature algorithms should conform to + * the algorithm constraints specified by + * {@link SSLParameters#getAlgorithmConstraints getAlgorithmConstraints()} + * method in <code>SSLParameters</code>. + * + * @return An array of supported signature algorithms, in descending + * order of preference. The return value is an empty array if + * no signature algorithm is supported. + * + * @see SSLParameters#getAlgorithmConstraints + */ + public abstract String[] getLocalSupportedSignatureAlgorithms(); + + /** + * Obtains an array of supported signature algorithms that the peer is + * able to use. + * <p> + * Note: this method is used to indicate to the local side which signature + * algorithms may be used for digital signatures in TLS 1.2. It is + * not meaningful for TLS versions prior to 1.2. + * <p> + * The signature algorithm name must be a standard Java Security + * name (such as "SHA1withRSA", "SHA256withECDSA", and so on). + * See Appendix A in the <a href= + * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> + * Java Cryptography Architecture API Specification & Reference </a> + * for information about standard algorithm names. + * + * @return An array of supported signature algorithms, in descending + * order of preference. The return value is an empty array if + * the peer has not sent the supported signature algorithms. + * + * @see X509KeyManager + * @see X509ExtendedKeyManager + */ + public abstract String[] getPeerSupportedSignatureAlgorithms(); +}
--- a/jdk/src/share/classes/javax/net/ssl/HttpsURLConnection.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/net/ssl/HttpsURLConnection.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. 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 @@ -188,19 +188,8 @@ * <p> * The default implementation will deny such connections. */ - private static HostnameVerifier defaultHostnameVerifier; - - /** - * Initialize the default <code>HostnameVerifier</code>. - */ - static { - try { - defaultHostnameVerifier = - new sun.net.www.protocol.https.DefaultHostnameVerifier(); - } catch (NoClassDefFoundError e) { - defaultHostnameVerifier = new DefaultHostnameVerifier(); - } - } + private static HostnameVerifier defaultHostnameVerifier = + new DefaultHostnameVerifier(); /* * The initial default <code>HostnameVerifier</code>. Should be
--- a/jdk/src/share/classes/javax/net/ssl/SSLEngine.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/net/ssl/SSLEngine.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. 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 @@ -968,6 +968,47 @@ /** + * Returns the {@code SSLSession} being constructed during a SSL/TLS + * handshake. + * <p> + * TLS protocols may negotiate parameters that are needed when using + * an instance of this class, but before the {@code SSLSession} has + * been completely initialized and made available via {@code getSession}. + * For example, the list of valid signature algorithms may restrict + * the type of certificates that can used during TrustManager + * decisions, or the maximum TLS fragment packet sizes can be + * resized to better support the network environment. + * <p> + * This method provides early access to the {@code SSLSession} being + * constructed. Depending on how far the handshake has progressed, + * some data may not yet be available for use. For example, if a + * remote server will be sending a Certificate chain, but that chain + * has yet not been processed, the {@code getPeerCertificates} + * method of {@code SSLSession} will throw a + * SSLPeerUnverifiedException. Once that chain has been processed, + * {@code getPeerCertificates} will return the proper value. + * + * @see SSLSocket + * @see SSLSession + * @see ExtendedSSLSession + * @see X509ExtendedKeyManager + * @see X509ExtendedTrustManager + * + * @return null if this instance is not currently handshaking, or + * if the current handshake has not progressed far enough to + * create a basic SSLSession. Otherwise, this method returns the + * {@code SSLSession} currently being negotiated. + * @throws UnsupportedOperationException if the underlying provider + * does not implement the operation. + * + * @since 1.7 + */ + public SSLSession getHandshakeSession() { + throw new UnsupportedOperationException(); + } + + + /** * Initiates handshaking (initial or renegotiation) on this SSLEngine. * <P> * This method is not needed for the initial handshake, as the
--- a/jdk/src/share/classes/javax/net/ssl/SSLParameters.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/net/ssl/SSLParameters.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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,24 +25,29 @@ package javax.net.ssl; +import java.security.AlgorithmConstraints; + /** * Encapsulates parameters for an SSL/TLS connection. The parameters * are the list of ciphersuites to be accepted in an SSL/TLS handshake, - * the list of protocols to be allowed, and whether SSL/TLS servers should - * request or require client authentication. - * - * <p>SSLParameters can be created via the constructors in this class. + * the list of protocols to be allowed, the endpoint identification + * algorithm during SSL/TLS handshaking, the algorithm constraints and + * whether SSL/TLS servers should request or require client authentication. + * <p> + * SSLParameters can be created via the constructors in this class. * Objects can also be obtained using the <code>getSSLParameters()</code> * methods in * {@link SSLSocket#getSSLParameters SSLSocket} and + * {@link SSLServerSocket#getSSLParameters SSLServerSocket} and * {@link SSLEngine#getSSLParameters SSLEngine} or the * {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and * {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()} * methods in <code>SSLContext</code>. - * - * <P>SSLParameters can be applied to a connection via the methods + * <p> + * SSLParameters can be applied to a connection via the methods * {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and - * {@link SSLEngine#setSSLParameters SSLEngine.getSSLParameters()}. + * {@link SSLServerSocket#setSSLParameters SSLServerSocket.setSSLParameters()} + * and {@link SSLEngine#setSSLParameters SSLEngine.getSSLParameters()}. * * @see SSLSocket * @see SSLEngine @@ -56,11 +61,13 @@ private String[] protocols; private boolean wantClientAuth; private boolean needClientAuth; + private String identificationAlgorithm; + private AlgorithmConstraints algorithmConstraints; /** * Constructs SSLParameters. - * - * <p>The cipherSuites and protocols values are set to <code>null</code>, + * <p> + * The cipherSuites and protocols values are set to <code>null</code>, * wantClientAuth and needClientAuth are set to <code>false</code>. */ public SSLParameters() { @@ -69,6 +76,7 @@ /** * Constructs SSLParameters from the specified array of ciphersuites. + * <p> * Calling this constructor is equivalent to calling the no-args * constructor followed by * <code>setCipherSuites(cipherSuites);</code>. @@ -82,6 +90,7 @@ /** * Constructs SSLParameters from the specified array of ciphersuites * and protocols. + * <p> * Calling this constructor is equivalent to calling the no-args * constructor followed by * <code>setCipherSuites(cipherSuites); setProtocols(protocols);</code>. @@ -178,4 +187,71 @@ this.needClientAuth = needClientAuth; } + /** + * Returns the cryptographic algorithm constraints. + * + * @return the cryptographic algorithm constraints, or null if the + * constraints have not been set + * + * @see #setAlgorithmConstraints(AlgorithmConstraints) + * + * @since 1.7 + */ + public AlgorithmConstraints getAlgorithmConstraints() { + return algorithmConstraints; + } + + /** + * Sets the cryptographic algorithm constraints, which will be used + * in addition to any configured by the runtime environment. + * <p> + * If the <code>constraints</code> parameter is non-null, every + * cryptographic algorithm, key and algorithm parameters used in the + * SSL/TLS handshake must be permitted by the constraints. + * + * @param constraints the algorithm constraints (or null) + * + * @since 1.7 + */ + public void setAlgorithmConstraints(AlgorithmConstraints constraints) { + // the constraints object is immutable + this.algorithmConstraints = constraints; + } + + /** + * Gets the endpoint identification algorithm. + * + * @return the endpoint identification algorithm, or null if none + * has been set. + * + * @see X509ExtendedTrustManager + * @see #setEndpointIdentificationAlgorithm(String) + * + * @since 1.7 + */ + public String getEndpointIdentificationAlgorithm() { + return identificationAlgorithm; + } + + /** + * Sets the endpoint identification algorithm. + * <p> + * If the <code>algorithm</code> parameter is non-null or non-empty, the + * endpoint identification/verification procedures must be handled during + * SSL/TLS handshaking. This is to prevent man-in-the-middle attacks. + * + * @param algorithm The standard string name of the endpoint + * identification algorithm (or null). See Appendix A in the <a href= + * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> + * Java Cryptography Architecture API Specification & Reference </a> + * for information about standard algorithm names. + * + * @see X509ExtendedTrustManager + * + * @since 1.7 + */ + public void setEndpointIdentificationAlgorithm(String algorithm) { + this.identificationAlgorithm = algorithm; + } + }
--- a/jdk/src/share/classes/javax/net/ssl/SSLServerSocket.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/net/ssl/SSLServerSocket.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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 @@ -56,8 +56,8 @@ * @since 1.4 * @author David Brownell */ -public abstract class SSLServerSocket extends ServerSocket -{ +public abstract class SSLServerSocket extends ServerSocket { + /** * Used only by subclasses. * <P> @@ -449,8 +449,79 @@ * * @return true indicates that sessions may be created; this * is the default. false indicates that an existing - * session must be resumed. + * session must be resumed * @see #setEnableSessionCreation(boolean) */ public abstract boolean getEnableSessionCreation(); + + /** + * Returns the SSLParameters in effect for newly accepted connections. + * The ciphersuites and protocols of the returned SSLParameters + * are always non-null. + * + * @return the SSLParameters in effect for newly accepted connections + * + * @see #setSSLParameters(SSLParameters) + * + * @since 1.7 + */ + public SSLParameters getSSLParameters() { + SSLParameters parameters = new SSLParameters(); + + parameters.setCipherSuites(getEnabledCipherSuites()); + parameters.setProtocols(getEnabledProtocols()); + if (getNeedClientAuth()) { + parameters.setNeedClientAuth(true); + } else if (getWantClientAuth()) { + parameters.setWantClientAuth(true); + } + + return parameters; + } + + /** + * Applies SSLParameters to newly accepted connections. + * + * <p>This means: + * <ul> + * <li>if <code>params.getCipherSuites()</code> is non-null, + * <code>setEnabledCipherSuites()</code> is called with that value + * <li>if <code>params.getProtocols()</code> is non-null, + * <code>setEnabledProtocols()</code> is called with that value + * <li>if <code>params.getNeedClientAuth()</code> or + * <code>params.getWantClientAuth()</code> return <code>true</code>, + * <code>setNeedClientAuth(true)</code> and + * <code>setWantClientAuth(true)</code> are called, respectively; + * otherwise <code>setWantClientAuth(false)</code> is called. + * </ul> + * + * @param params the parameters + * @throws IllegalArgumentException if the setEnabledCipherSuites() or + * the setEnabledProtocols() call fails + * + * @see #getSSLParameters() + * + * @since 1.7 + */ + public void setSSLParameters(SSLParameters params) { + String[] s; + s = params.getCipherSuites(); + if (s != null) { + setEnabledCipherSuites(s); + } + + s = params.getProtocols(); + if (s != null) { + setEnabledProtocols(s); + } + + if (params.getNeedClientAuth()) { + setNeedClientAuth(true); + } else if (params.getWantClientAuth()) { + setWantClientAuth(true); + } else { + setWantClientAuth(false); + } + } + }
--- a/jdk/src/share/classes/javax/net/ssl/SSLSocket.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/net/ssl/SSLSocket.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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 @@ -371,6 +371,51 @@ /** + * Returns the {@code SSLSession} being constructed during a SSL/TLS + * handshake. + * <p> + * TLS protocols may negotiate parameters that are needed when using + * an instance of this class, but before the {@code SSLSession} has + * been completely initialized and made available via {@code getSession}. + * For example, the list of valid signature algorithms may restrict + * the type of certificates that can used during TrustManager + * decisions, or the maximum TLS fragment packet sizes can be + * resized to better support the network environment. + * <p> + * This method provides early access to the {@code SSLSession} being + * constructed. Depending on how far the handshake has progressed, + * some data may not yet be available for use. For example, if a + * remote server will be sending a Certificate chain, but that chain + * has yet not been processed, the {@code getPeerCertificates} + * method of {@code SSLSession} will throw a + * SSLPeerUnverifiedException. Once that chain has been processed, + * {@code getPeerCertificates} will return the proper value. + * <p> + * Unlike {@link #getSession()}, this method does not initiate the + * initial handshake and does not block until handshaking is + * complete. + * + * @see SSLEngine + * @see SSLSession + * @see ExtendedSSLSession + * @see X509ExtendedKeyManager + * @see X509ExtendedTrustManager + * + * @return null if this instance is not currently handshaking, or + * if the current handshake has not progressed far enough to + * create a basic SSLSession. Otherwise, this method returns the + * {@code SSLSession} currently being negotiated. + * @throws UnsupportedOperationException if the underlying provider + * does not implement the operation. + * + * @since 1.7 + */ + public SSLSession getHandshakeSession() { + throw new UnsupportedOperationException(); + } + + + /** * Registers an event listener to receive notifications that an * SSL handshake has completed on this connection. *
--- a/jdk/src/share/classes/javax/net/ssl/SSLSocketFactory.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/net/ssl/SSLSocketFactory.java Tue Nov 09 22:53:18 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. 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 @@ -30,6 +30,7 @@ import javax.net.SocketFactory; import java.io.IOException; import java.security.*; +import java.util.Locale; import sun.security.action.GetPropertyAction; @@ -50,7 +51,8 @@ static { String s = java.security.AccessController.doPrivileged( - new GetPropertyAction("javax.net.debug", "")).toLowerCase(); + new GetPropertyAction("javax.net.debug", "")).toLowerCase( + Locale.ENGLISH); DEBUG = s.contains("all") || s.contains("ssl"); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/javax/net/ssl/X509ExtendedTrustManager.java Tue Nov 09 22:53:18 2010 -0800 @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.net.ssl; + +import java.net.Socket; +import javax.net.ssl.X509TrustManager; + +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; + +/** + * Extensions to the <code>X509TrustManager</code> interface to support + * SSL/TLS connection sensitive trust management. + * <p> + * To prevent man-in-the-middle attacks, hostname checks can be done + * to verify that the hostname in an end-entity certificate matches the + * targeted hostname. TLS does not require such checks, but some protocols + * over TLS (such as HTTPS) do. In earlier versions of the JDK, the + * certificate chain checks were done at the SSL/TLS layer, and the hostname + * verification checks were done at the layer over TLS. This class allows + * for the checking to be done during a single call to this class. + * <p> + * RFC 2830 defines the server identification specification for the "LDAPS" + * algorithm. RFC 2818 defines both the server identification and the + * client identification specification for the "HTTPS" algorithm. + * + * @see X509TrustManager + * @see HostnameVerifier + * + * @since 1.7 + */ +public abstract class X509ExtendedTrustManager implements X509TrustManager { + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * The authentication type is determined by the actual certificate + * used. For instance, if RSAPublicKey is used, the authType + * should be "RSA". Checking is case-sensitive. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the endpoint identification + * algorithm of the <code>SSLParameters</code> is non-empty, to prevent + * man-in-the-middle attacks, the address that the <code>socket</code> + * connected to should be checked against the peer's identity presented + * in the end-entity X509 certificate, as specified in the endpoint + * identification algorithm. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the algorithm constraints of the + * <code>SSLParameters</code> is non-null, for every certificate in the + * certification path, fields such as subject public key, the signature + * algorithm, key usage, extended key usage, etc. need to conform to the + * algorithm constraints in place on this socket. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param socket the socket used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkClientTrusted(X509Certificate[] chain, + String authType, Socket socket) throws CertificateException; + + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * The authentication type is the key exchange algorithm portion + * of the cipher suites represented as a String, such as "RSA", + * "DHE_DSS". Note: for some exportable cipher suites, the key + * exchange algorithm is determined at run time during the + * handshake. For instance, for TLS_RSA_EXPORT_WITH_RC4_40_MD5, + * the authType should be RSA_EXPORT when an ephemeral RSA key is + * used for the key exchange, and RSA when the key from the server + * certificate is used. Checking is case-sensitive. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the endpoint identification + * algorithm of the <code>SSLParameters</code> is non-empty, to prevent + * man-in-the-middle attacks, the address that the <code>socket</code> + * connected to should be checked against the peer's identity presented + * in the end-entity X509 certificate, as specified in the endpoint + * identification algorithm. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the algorithm constraints of the + * <code>SSLParameters</code> is non-null, for every certificate in the + * certification path, fields such as subject public key, the signature + * algorithm, key usage, extended key usage, etc. need to conform to the + * algorithm constraints in place on this socket. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param socket the socket used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkServerTrusted(X509Certificate[] chain, + String authType, Socket socket) throws CertificateException; + + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * The authentication type is determined by the actual certificate + * used. For instance, if RSAPublicKey is used, the authType + * should be "RSA". Checking is case-sensitive. + * <p> + * If the <code>engine</code> parameter is available, and the endpoint + * identification algorithm of the <code>SSLParameters</code> is + * non-empty, to prevent man-in-the-middle attacks, the address that + * the <code>engine</code> connected to should be checked against + * the peer's identity presented in the end-entity X509 certificate, + * as specified in the endpoint identification algorithm. + * <p> + * If the <code>engine</code> parameter is available, and the algorithm + * constraints of the <code>SSLParameters</code> is non-null, for every + * certificate in the certification path, fields such as subject public + * key, the signature algorithm, key usage, extended key usage, etc. + * need to conform to the algorithm constraints in place on this engine. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param engine the engine used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkClientTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) throws CertificateException; + + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * The authentication type is the key exchange algorithm portion + * of the cipher suites represented as a String, such as "RSA", + * "DHE_DSS". Note: for some exportable cipher suites, the key + * exchange algorithm is determined at run time during the + * handshake. For instance, for TLS_RSA_EXPORT_WITH_RC4_40_MD5, + * the authType should be RSA_EXPORT when an ephemeral RSA key is + * used for the key exchange, and RSA when the key from the server + * certificate is used. Checking is case-sensitive. + * <p> + * If the <code>engine</code> parameter is available, and the endpoint + * identification algorithm of the <code>SSLParameters</code> is + * non-empty, to prevent man-in-the-middle attacks, the address that + * the <code>engine</code> connected to should be checked against + * the peer's identity presented in the end-entity X509 certificate, + * as specified in the endpoint identification algorithm. + * <p> + * If the <code>engine</code> parameter is available, and the algorithm + * constraints of the <code>SSLParameters</code> is non-null, for every + * certificate in the certification path, fields such as subject public + * key, the signature algorithm, key usage, extended key usage, etc. + * need to conform to the algorithm constraints in place on this engine. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param engine the engine used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkServerTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) throws CertificateException; + +}
--- a/jdk/src/share/classes/javax/swing/BorderFactory.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/BorderFactory.java Tue Nov 09 22:53:18 2010 -0800 @@ -24,8 +24,10 @@ */ package javax.swing; +import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; +import java.awt.Paint; import javax.swing.border.*; /** @@ -636,4 +638,125 @@ Icon tileIcon) { return new MatteBorder(top, left, bottom, right, tileIcon); } + +//// StrokeBorder ////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + /** + * Creates a border of the specified {@code stroke}. + * The component's foreground color will be used to render the border. + * + * @param stroke the {@link BasicStroke} object used to stroke a shape + * @return the {@code Border} object + * + * @throws NullPointerException if the specified {@code stroke} is {@code null} + * + * @since 1.7 + */ + public static Border createStrokeBorder(BasicStroke stroke) { + return new StrokeBorder(stroke); + } + + /** + * Creates a border of the specified {@code stroke} and {@code paint}. + * If the specified {@code paint} is {@code null}, + * the component's foreground color will be used to render the border. + * + * @param stroke the {@link BasicStroke} object used to stroke a shape + * @param paint the {@link Paint} object used to generate a color + * @return the {@code Border} object + * + * @throws NullPointerException if the specified {@code stroke} is {@code null} + * + * @since 1.7 + */ + public static Border createStrokeBorder(BasicStroke stroke, Paint paint) { + return new StrokeBorder(stroke, paint); + } + +//// DashedBorder ////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + private static Border sharedDashedBorder; + + /** + * Creates a dashed border of the specified {@code paint}. + * If the specified {@code paint} is {@code null}, + * the component's foreground color will be used to render the border. + * The width of a dash line is equal to {@code 1}. + * The relative length of a dash line and + * the relative spacing between dash lines are equal to {@code 1}. + * A dash line is not rounded. + * + * @param paint the {@link Paint} object used to generate a color + * @return the {@code Border} object + * + * @since 1.7 + */ + public static Border createDashedBorder(Paint paint) { + return createDashedBorder(paint, 1.0f, 1.0f, 1.0f, false); + } + + /** + * Creates a dashed border of the specified {@code paint}, + * relative {@code length}, and relative {@code spacing}. + * If the specified {@code paint} is {@code null}, + * the component's foreground color will be used to render the border. + * The width of a dash line is equal to {@code 1}. + * A dash line is not rounded. + * + * @param paint the {@link Paint} object used to generate a color + * @param length the relative length of a dash line + * @param spacing the relative spacing between dash lines + * @return the {@code Border} object + * + * @throws IllegalArgumentException if the specified {@code length} is less than {@code 1}, or + * if the specified {@code spacing} is less than {@code 0} + * @since 1.7 + */ + public static Border createDashedBorder(Paint paint, float length, float spacing) { + return createDashedBorder(paint, 1.0f, length, spacing, false); + } + + /** + * Creates a dashed border of the specified {@code paint}, {@code thickness}, + * line shape, relative {@code length}, and relative {@code spacing}. + * If the specified {@code paint} is {@code null}, + * the component's foreground color will be used to render the border. + * + * @param paint the {@link Paint} object used to generate a color + * @param thickness the width of a dash line + * @param length the relative length of a dash line + * @param spacing the relative spacing between dash lines + * @param rounded whether or not line ends should be round + * @return the {@code Border} object + * + * @throws IllegalArgumentException if the specified {@code thickness} is less than {@code 1}, or + * if the specified {@code length} is less than {@code 1}, or + * if the specified {@code spacing} is less than {@code 0} + * @since 1.7 + */ + public static Border createDashedBorder(Paint paint, float thickness, float length, float spacing, boolean rounded) { + boolean shared = !rounded && (paint == null) && (thickness == 1.0f) && (length == 1.0f) && (spacing == 1.0f); + if (shared && (sharedDashedBorder != null)) { + return sharedDashedBorder; + } + if (thickness < 1.0f) { + throw new IllegalArgumentException("thickness is less than 1"); + } + if (length < 1.0f) { + throw new IllegalArgumentException("length is less than 1"); + } + if (spacing < 0.0f) { + throw new IllegalArgumentException("spacing is less than 0"); + } + int cap = rounded ? BasicStroke.CAP_ROUND : BasicStroke.CAP_SQUARE; + int join = rounded ? BasicStroke.JOIN_ROUND : BasicStroke.JOIN_MITER; + float[] array = { thickness * (length - 1.0f), thickness * (spacing + 1.0f) }; + Border border = createStrokeBorder(new BasicStroke(thickness, cap, join, thickness * 2.0f, array, 0.0f), paint); + if (shared) { + sharedDashedBorder = border; + } + return border; + } }
--- a/jdk/src/share/classes/javax/swing/DebugGraphics.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/DebugGraphics.java Tue Nov 09 22:53:18 2010 -0800 @@ -1322,13 +1322,11 @@ } String toShortString() { - StringBuffer buffer = new StringBuffer("Graphics" + (isDrawingBuffer() ? "<B>" : "") + "(" + graphicsID + "-" + debugOptions + ")"); - return buffer.toString(); + return "Graphics" + (isDrawingBuffer() ? "<B>" : "") + "(" + graphicsID + "-" + debugOptions + ")"; } String pointToString(int x, int y) { - StringBuffer buffer = new StringBuffer("(" + x + ", " + y + ")"); - return buffer.toString(); + return "(" + x + ", " + y + ")"; } /** Enables/disables diagnostic information about every graphics
--- a/jdk/src/share/classes/javax/swing/JComponent.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/JComponent.java Tue Nov 09 22:53:18 2010 -0800 @@ -4783,21 +4783,11 @@ * @param y the y value of the dirty region * @param width the width of the dirty region * @param height the height of the dirty region + * @see #isPaintingOrigin() * @see java.awt.Component#isShowing * @see RepaintManager#addDirtyRegion */ public void repaint(long tm, int x, int y, int width, int height) { - Container p = this; - while ((p = p.getParent()) instanceof JComponent) { - JComponent jp = (JComponent) p; - if (jp.isPaintingOrigin()) { - Rectangle rectangle = SwingUtilities.convertRectangle( - this, new Rectangle(x, y, width, height), jp); - jp.repaint(tm, - rectangle.x, rectangle.y, rectangle.width, rectangle.height); - return; - } - } RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, height); } @@ -4808,6 +4798,7 @@ * currently pending events have been dispatched. * * @param r a <code>Rectangle</code> containing the dirty region + * @see #isPaintingOrigin() * @see java.awt.Component#isShowing * @see RepaintManager#addDirtyRegion */ @@ -4912,13 +4903,19 @@ } /** - * Returns true if a paint triggered on a child component should cause + * Returns {@code true} if a paint triggered on a child component should cause * painting to originate from this Component, or one of its ancestors. + * <p/> + * Calling {@link JComponent#repaint} on a Swing component will be delegated to + * the first ancestor which {@code isPaintingOrigin()} returns {@true}, + * if there are any. + * <p/> + * {@code JComponent} subclasses that need to be repainted when any of their + * children are repainted should override this method to return {@code true}. * - * @return true if painting should originate from this Component or - * one of its ancestors. - */ - boolean isPaintingOrigin() { + * @return always returns {@code false} + */ + protected boolean isPaintingOrigin() { return false; }
--- a/jdk/src/share/classes/javax/swing/JDialog.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/JDialog.java Tue Nov 09 22:53:18 2010 -0800 @@ -303,10 +303,9 @@ * @param modal specifies whether dialog blocks user input to other top-level * windows when shown. If {@code true}, the modality type property is set to * {@code DEFAULT_MODALITY_TYPE}, otherwise the dialog is modeless. - * @param gc the {@code GraphicsConfiguration} - * of the target screen device. If {@code gc} is - * {@code null}, the same - * {@code GraphicsConfiguration} as the owning Frame is used. + * @param gc the {@code GraphicsConfiguration} of the target screen device; + * if {@code null}, the default system {@code GraphicsConfiguration} + * is assumed * @throws HeadlessException if {@code GraphicsEnvironment.isHeadless()} * returns {@code true}. * @see java.awt.Dialog.ModalityType @@ -441,10 +440,9 @@ * @param modal specifies whether dialog blocks user input to other top-level * windows when shown. If {@code true}, the modality type property is set to * {@code DEFAULT_MODALITY_TYPE}, otherwise the dialog is modeless - * @param gc the {@code GraphicsConfiguration} - * of the target screen device. If {@code gc} is - * {@code null}, the same - * {@code GraphicsConfiguration} as the owning Dialog is used. + * @param gc the {@code GraphicsConfiguration} of the target screen device; + * if {@code null}, the default system {@code GraphicsConfiguration} + * is assumed * @throws HeadlessException if {@code GraphicsEnvironment.isHeadless()} * returns {@code true}. * @see java.awt.Dialog.ModalityType @@ -612,10 +610,8 @@ * windows when shown. {@code null} value and unsupported modality * types are equivalent to {@code MODELESS} * @param gc the {@code GraphicsConfiguration} of the target screen device; - * if {@code null}, the {@code GraphicsConfiguration} from the owning - * window is used; if {@code owner} is also {@code null}, the - * system default {@code GraphicsConfiguration} is assumed - * + * if {@code null}, the default system {@code GraphicsConfiguration} + * is assumed * @throws IllegalArgumentException * if the {@code owner} is not an instance of {@link java.awt.Dialog Dialog} * or {@link java.awt.Frame Frame}
--- a/jdk/src/share/classes/javax/swing/JLayer.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/JLayer.java Tue Nov 09 22:53:18 2010 -0800 @@ -384,7 +384,7 @@ * @return true * @see JComponent#isPaintingOrigin() */ - boolean isPaintingOrigin() { + protected boolean isPaintingOrigin() { return true; }
--- a/jdk/src/share/classes/javax/swing/JViewport.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/JViewport.java Tue Nov 09 22:53:18 2010 -0800 @@ -637,14 +637,14 @@ } /** - * Returns true if scroll mode is a BACKINGSTORE_SCROLL_MODE to cause - * painting to originate from <code>JViewport</code>, or one of its - * ancestors. Otherwise returns false. + * Returns true if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE} to cause + * painting to originate from {@code JViewport}, or one of its + * ancestors. Otherwise returns {@code false}. * - * @return true if if scroll mode is a BACKINGSTORE_SCROLL_MODE. + * @return true if if scroll mode is a {@code BACKINGSTORE_SCROLL_MODE}. * @see JComponent#isPaintingOrigin() */ - boolean isPaintingOrigin() { + protected boolean isPaintingOrigin() { return scrollMode == BACKINGSTORE_SCROLL_MODE; }
--- a/jdk/src/share/classes/javax/swing/RepaintManager.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/RepaintManager.java Tue Nov 09 22:53:18 2010 -0800 @@ -438,6 +438,7 @@ * @param y Y coordinate of the region to repaint * @param w Width of the region to repaint * @param h Height of the region to repaint + * @see JComponent#isPaintingOrigin() * @see JComponent#repaint */ public void addDirtyRegion(JComponent c, int x, int y, int w, int h) @@ -447,6 +448,16 @@ delegate.addDirtyRegion(c, x, y, w, h); return; } + Container p = c; + while ((p = p.getParent()) instanceof JComponent) { + JComponent jp = (JComponent) p; + if (jp.isPaintingOrigin()) { + Rectangle rectangle = SwingUtilities.convertRectangle( + c, new Rectangle(x, y, w, h), jp); + jp.repaint(0, rectangle.x, rectangle.y, rectangle.width, rectangle.height); + return; + } + } addDirtyRegion0(c, x, y, w, h); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/javax/swing/border/StrokeBorder.java Tue Nov 09 22:53:18 2010 -0800 @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.swing.border; + +import java.awt.BasicStroke; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Paint; +import java.awt.RenderingHints; +import java.awt.geom.Rectangle2D; +import java.beans.ConstructorProperties; + +/** + * A class which implements a border of an arbitrary stroke. + * <p> + * <strong>Warning:</strong> + * Serialized objects of this class will not be compatible with + * future Swing releases. The current serialization support is + * appropriate for short term storage or RMI + * between applications running the same version of Swing. + * As of 1.4, support for long term storage of all JavaBeans™ + * has been added to the <code>java.beans</code> package. + * Please see {@link java.beans.XMLEncoder}. + * + * @author Sergey A. Malenkov + * + * @since 1.7 + */ +public class StrokeBorder extends AbstractBorder { + private final BasicStroke stroke; + private final Paint paint; + + /** + * Creates a border of the specified {@code stroke}. + * The component's foreground color will be used to render the border. + * + * @param stroke the {@link BasicStroke} object used to stroke a shape + * + * @throws NullPointerException if the specified {@code stroke} is {@code null} + */ + public StrokeBorder(BasicStroke stroke) { + this(stroke, null); + } + + /** + * Creates a border of the specified {@code stroke} and {@code paint}. + * If the specified {@code paint} is {@code null}, + * the component's foreground color will be used to render the border. + * + * @param stroke the {@link BasicStroke} object used to stroke a shape + * @param paint the {@link Paint} object used to generate a color + * + * @throws NullPointerException if the specified {@code stroke} is {@code null} + */ + @ConstructorProperties({ "stroke", "paint" }) + public StrokeBorder(BasicStroke stroke, Paint paint) { + if (stroke == null) { + throw new NullPointerException("border's stroke"); + } + this.stroke = stroke; + this.paint = paint; + } + + /** + * Paints the border for the specified component + * with the specified position and size. + * + * @param c the component for which this border is being painted + * @param g the paint graphics + * @param x the x position of the painted border + * @param y the y position of the painted border + * @param width the width of the painted border + * @param height the height of the painted border + * + * @throws NullPointerException if the specified {@code c} or {@code g} are {@code null} + */ + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + float size = this.stroke.getLineWidth(); + if (size > 0.0f) { + g = g.create(); + if (g instanceof Graphics2D) { + Graphics2D g2d = (Graphics2D) g; + g2d.setStroke(this.stroke); + g2d.setPaint(this.paint != null ? this.paint : c.getForeground()); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.draw(new Rectangle2D.Float(x + size / 2, y + size / 2, width - size, height - size)); + } + g.dispose(); + } + } + + /** + * Reinitializes the {@code insets} parameter + * with this border's current insets. + * All insets are equal to the line width of the stroke. + * + * @param c the component for which this border insets value applies + * @param insets the {@code Insets} object to be reinitialized + * @return the reinitialized {@code insets} parameter + * + * @throws NullPointerException if the specified {@code insets} is {@code null} + */ + @Override + public Insets getBorderInsets(Component c, Insets insets) { + int size = (int) Math.ceil(this.stroke.getLineWidth()); + insets.set(size, size, size, size); + return insets; + } + + /** + * Returns the {@link BasicStroke} object used to stroke a shape + * during the border rendering. + * + * @return the {@link BasicStroke} object + */ + public BasicStroke getStroke() { + return this.stroke; + } + + /** + * Returns the {@link Paint} object used to generate a color + * during the border rendering. + * + * @return the {@link Paint} object or {@code null} + * if the {@code paint} parameter is not set + */ + public Paint getPaint() { + return this.paint; + } +}
--- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicScrollPaneUI.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicScrollPaneUI.java Tue Nov 09 22:53:18 2010 -0800 @@ -362,6 +362,14 @@ * @since 1.6 */ public int getBaseline(JComponent c, int width, int height) { + if (c == null) { + throw new NullPointerException("Component must be non-null"); + } + + if (width < 0 || height < 0) { + throw new IllegalArgumentException("Width and height must be >= 0"); + } + JViewport viewport = scrollpane.getViewport(); Insets spInsets = scrollpane.getInsets(); int y = spInsets.top;
--- a/jdk/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java Tue Nov 09 22:53:18 2010 -0800 @@ -115,10 +115,7 @@ return new SynthTabbedPaneUI(); } - private SynthTabbedPaneUI() { - } - - private boolean scrollableTabLayoutEnabled() { + private boolean scrollableTabLayoutEnabled() { return (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT); }
--- a/jdk/src/share/classes/javax/swing/table/DefaultTableCellRenderer.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/table/DefaultTableCellRenderer.java Tue Nov 09 22:53:18 2010 -0800 @@ -186,6 +186,9 @@ */ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + if (table == null) { + return this; + } Color fg = null; Color bg = null;
--- a/jdk/src/share/classes/javax/swing/text/DefaultCaret.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/DefaultCaret.java Tue Nov 09 22:53:18 2010 -0800 @@ -1334,13 +1334,13 @@ && component.getClientProperty("JPasswordField.cutCopyAllowed") != Boolean.TRUE) { //fix for 4793761 - StringBuffer txt = null; + StringBuilder txt = null; char echoChar = ((JPasswordField)component).getEchoChar(); int p0 = Math.min(getDot(), getMark()); int p1 = Math.max(getDot(), getMark()); for (int i = p0; i < p1; i++) { if (txt == null) { - txt = new StringBuffer(); + txt = new StringBuilder(); } txt.append(echoChar); } @@ -1675,7 +1675,6 @@ } return; } - int adjust = 0; int offset = e.getOffset(); int length = e.getLength(); int newDot = dot; @@ -1759,7 +1758,6 @@ } int offs0 = e.getOffset(); int offs1 = offs0 + e.getLength(); - int adjust = 0; int newDot = dot; boolean adjustDotBias = false; int newMark = mark;
--- a/jdk/src/share/classes/javax/swing/text/DefaultStyledDocument.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/DefaultStyledDocument.java Tue Nov 09 22:53:18 2010 -0800 @@ -132,7 +132,7 @@ // install the content Content c = getContent(); int n = data.length; - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { ElementSpec es = data[i]; if (es.getLength() > 0) { @@ -191,7 +191,7 @@ // install the content Content c = getContent(); int n = data.length; - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { ElementSpec es = data[i]; if (es.getLength() > 0) {
--- a/jdk/src/share/classes/javax/swing/text/InternationalFormatter.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/InternationalFormatter.java Tue Nov 09 22:53:18 2010 -0800 @@ -30,7 +30,6 @@ import java.text.AttributedCharacterIterator.Attribute; import java.util.*; import javax.swing.*; -import javax.swing.text.*; /** * <code>InternationalFormatter</code> extends <code>DefaultFormatter</code>, @@ -875,7 +874,6 @@ (f instanceof AttributedCharacterIterator.Attribute)) { AttributedCharacterIterator.Attribute field = (AttributedCharacterIterator.Attribute)f; - int index = 0; iterator.first(); while (iterator.getIndex() < start) {
--- a/jdk/src/share/classes/javax/swing/text/JTextComponent.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/JTextComponent.java Tue Nov 09 22:53:18 2010 -0800 @@ -35,10 +35,7 @@ import java.util.Hashtable; import java.util.Enumeration; import java.util.Vector; -import java.util.Iterator; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; import java.util.concurrent.*; @@ -4058,7 +4055,7 @@ private static final Object KEYMAP_TABLE = new StringBuilder("JTextComponent_KeymapTable"); - private JTextComponent editor; + // // member variables used for on-the-spot input method // editing style support @@ -4748,14 +4745,14 @@ processKeyEvent(ke); } } else { - StringBuffer strBuf = new StringBuffer(); + StringBuilder strBuf = new StringBuilder(); for (char c = text.current(); commitCount > 0; c = text.next(), commitCount--) { strBuf.append(c); } // map it to an ActionEvent - mapCommittedTextToAction(new String(strBuf)); + mapCommittedTextToAction(strBuf.toString()); } // Remember latest committed text end index @@ -4801,7 +4798,7 @@ private void createComposedTextAttribute(int composedIndex, AttributedCharacterIterator text) { Document doc = getDocument(); - StringBuffer strBuf = new StringBuffer(); + StringBuilder strBuf = new StringBuilder(); // create attributed string with no attributes for (char c = text.setIndex(composedIndex); @@ -4809,7 +4806,7 @@ strBuf.append(c); } - composedTextContent = new String(strBuf); + composedTextContent = strBuf.toString(); composedTextAttribute = new SimpleAttributeSet(); composedTextAttribute.addAttribute(StyleConstants.ComposedTextAttribute, new AttributedString(text, composedIndex, text.getEndIndex()));
--- a/jdk/src/share/classes/javax/swing/text/MaskFormatter.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/MaskFormatter.java Tue Nov 09 22:53:18 2010 -0800 @@ -29,7 +29,6 @@ import java.text.*; import java.util.*; import javax.swing.*; -import javax.swing.text.*; /** * <code>MaskFormatter</code> is used to format and edit strings. The behavior @@ -385,7 +384,7 @@ */ public String valueToString(Object value) throws ParseException { String sValue = (value == null) ? "" : value.toString(); - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); String placeholder = getPlaceholder(); int[] valueCounter = { 0 }; @@ -484,7 +483,7 @@ * Invokes <code>append</code> on the mask characters in * <code>mask</code>. */ - private void append(StringBuffer result, String value, int[] index, + private void append(StringBuilder result, String value, int[] index, String placeholder, MaskCharacter[] mask) throws ParseException { for (int counter = 0, maxCounter = mask.length; @@ -611,13 +610,13 @@ * Removes the literal characters from the passed in string. */ private String stripLiteralChars(String string) { - StringBuffer sb = null; + StringBuilder sb = null; int last = 0; for (int counter = 0, max = string.length(); counter < max; counter++){ if (isLiteral(counter)) { if (sb == null) { - sb = new StringBuffer(); + sb = new StringBuilder(); if (counter > 0) { sb.append(string.substring(0, counter)); } @@ -715,10 +714,10 @@ */ boolean canReplace(ReplaceHolder rh) { // This method is rather long, but much of the burden is in - // maintaining a String and swapping to a StringBuffer only if + // maintaining a String and swapping to a StringBuilder only if // absolutely necessary. if (!getAllowsInvalid()) { - StringBuffer replace = null; + StringBuilder replace = null; String text = rh.text; int tl = (text != null) ? text.length() : 0; @@ -737,7 +736,7 @@ char aChar = text.charAt(textIndex); if (aChar != getCharacter(rh.offset + counter, aChar)) { if (replace == null) { - replace = new StringBuffer(); + replace = new StringBuilder(); if (textIndex > 0) { replace.append(text.substring(0, textIndex)); } @@ -758,7 +757,7 @@ } } else if (textIndex > 0) { - replace = new StringBuffer(max); + replace = new StringBuilder(max); replace.append(text.substring(0, textIndex)); replace.append(getLiteral(rh.offset + counter)); if (textIndex < tl) { @@ -780,7 +779,7 @@ else if (textIndex >= tl) { // placeholder if (replace == null) { - replace = new StringBuffer(); + replace = new StringBuilder(); if (text != null) { replace.append(text); } @@ -863,7 +862,7 @@ * Appends the necessary character in <code>formatting</code> at * <code>index</code> to <code>buff</code>. */ - public void append(StringBuffer buff, String formatting, int[] index, + public void append(StringBuilder buff, String formatting, int[] index, String placeholder) throws ParseException { boolean inString = index[0] < formatting.length();
--- a/jdk/src/share/classes/javax/swing/text/NumberFormatter.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/NumberFormatter.java Tue Nov 09 22:53:18 2010 -0800 @@ -27,7 +27,6 @@ import java.lang.reflect.*; import java.text.*; import java.util.*; -import javax.swing.text.*; /** * <code>NumberFormatter</code> subclasses <code>InternationalFormatter</code> @@ -132,7 +131,7 @@ DecimalFormatSymbols dfs = getDecimalFormatSymbols(); if (dfs != null) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.append(dfs.getCurrencySymbol()); sb.append(dfs.getDecimalSeparator()); @@ -240,13 +239,6 @@ } /** - */ - private boolean isValidInsertionCharacter(char aChar) { - return (Character.isDigit(aChar) || specialChars.indexOf(aChar) != -1); - } - - - /** * Subclassed to return false if <code>text</code> contains in an invalid * character to insert, that is, it is not a digit * (<code>Character.isDigit()</code>) and @@ -403,28 +395,6 @@ } /** - * Returns true if the range offset to length identifies the only - * integer field. - */ - private boolean isOnlyIntegerField(int offset, int length) { - if (isValidMask()) { - int start = getAttributeStart(NumberFormat.Field.INTEGER); - - if (start != -1) { - AttributedCharacterIterator iterator = getIterator(); - - iterator.setIndex(start); - if (offset > start || iterator.getRunLimit( - NumberFormat.Field.INTEGER) > (offset + length)) { - return false; - } - return true; - } - } - return false; - } - - /** * Invoked to toggle the sign. For this to work the value class * must have a single arg constructor that takes a String. */
--- a/jdk/src/share/classes/javax/swing/text/PlainDocument.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/PlainDocument.java Tue Nov 09 22:53:18 2010 -0800 @@ -25,7 +25,6 @@ package javax.swing.text; import java.util.Vector; -import javax.swing.event.*; /** * A plain document that maintains no character attributes. The @@ -118,7 +117,7 @@ Object filterNewlines = getProperty("filterNewlines"); if ((filterNewlines instanceof Boolean) && filterNewlines.equals(Boolean.TRUE)) { if ((str != null) && (str.indexOf('\n') >= 0)) { - StringBuffer filtered = new StringBuffer(str); + StringBuilder filtered = new StringBuilder(str); int n = filtered.length(); for (int i = 0; i < n; i++) { if (filtered.charAt(i) == '\n') { @@ -204,11 +203,9 @@ } } if (hasBreaks) { - int rmCount = 1; removed.addElement(rmCandidate); if ((offset + length == rmOffs1) && (lastOffset != rmOffs1) && ((index+1) < lineMap.getElementCount())) { - rmCount += 1; Element e = lineMap.getElement(index+1); removed.addElement(e); rmOffs1 = e.getEndOffset();
--- a/jdk/src/share/classes/javax/swing/text/TabSet.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/TabSet.java Tue Nov 09 22:53:18 2010 -0800 @@ -199,7 +199,7 @@ */ public String toString() { int tabCount = getTabCount(); - StringBuffer buffer = new StringBuffer("[ "); + StringBuilder buffer = new StringBuilder("[ "); for(int counter = 0; counter < tabCount; counter++) { if(counter > 0)
--- a/jdk/src/share/classes/javax/swing/text/html/FormView.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/html/FormView.java Tue Nov 09 22:53:18 2010 -0800 @@ -362,7 +362,7 @@ */ public void actionPerformed(ActionEvent evt) { Element element = getElement(); - StringBuffer dataBuffer = new StringBuffer(); + StringBuilder dataBuffer = new StringBuilder(); HTMLDocument doc = (HTMLDocument)getDocument(); AttributeSet attr = element.getAttributes(); @@ -508,7 +508,7 @@ */ protected void imageSubmit(String imageData) { - StringBuffer dataBuffer = new StringBuffer(); + StringBuilder dataBuffer = new StringBuilder(); Element elem = getElement(); HTMLDocument hdoc = (HTMLDocument)elem.getDocument(); getFormData(dataBuffer); @@ -589,7 +589,7 @@ * @param targetElement the element that triggered the * form submission */ - void getFormData(StringBuffer buffer) { + private void getFormData(StringBuilder buffer) { Element formE = getFormElement(); if (formE != null) { ElementIterator it = new ElementIterator(formE); @@ -623,7 +623,7 @@ * data is loaded in name/value pairs. * */ - private void loadElementDataIntoBuffer(Element elem, StringBuffer buffer) { + private void loadElementDataIntoBuffer(Element elem, StringBuilder buffer) { AttributeSet attr = elem.getAttributes(); String name = (String)attr.getAttribute(HTML.Attribute.NAME); @@ -692,29 +692,6 @@ } if (path != null && path.length() > 0) { value = path; -/* - - try { - Reader reader = new BufferedReader(new FileReader(path)); - StringBuffer buffer = new StringBuffer(); - char[] cBuff = new char[1024]; - int read; - - try { - while ((read = reader.read(cBuff)) != -1) { - buffer.append(cBuff, 0, read); - } - } catch (IOException ioe) { - buffer = null; - } - try { - reader.close(); - } catch (IOException ioe) {} - if (buffer != null) { - value = buffer.toString(); - } - } catch (IOException ioe) {} -*/ } } return value; @@ -740,7 +717,7 @@ * form element. Basically, only items that are selected * and have their name attribute set are added to the buffer. */ - private void loadSelectData(AttributeSet attr, StringBuffer buffer) { + private void loadSelectData(AttributeSet attr, StringBuilder buffer) { String name = (String)attr.getAttribute(HTML.Attribute.NAME); if (name == null) { @@ -771,7 +748,7 @@ * URLEncoder.encode() method before being added to the * buffer. */ - private void appendBuffer(StringBuffer buffer, String name, String value) { + private void appendBuffer(StringBuilder buffer, String name, String value) { if (buffer.length() > 0) { buffer.append('&'); }
--- a/jdk/src/share/classes/javax/swing/text/html/MinimalHTMLWriter.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/html/MinimalHTMLWriter.java Tue Nov 09 22:53:18 2010 -0800 @@ -691,11 +691,11 @@ if (styleNameMapping == null) { return style; } - StringBuffer sb = null; + StringBuilder sb = null; for (int counter = style.length() - 1; counter >= 0; counter--) { if (!isValidCharacter(style.charAt(counter))) { if (sb == null) { - sb = new StringBuffer(style); + sb = new StringBuilder(style); } sb.setCharAt(counter, 'a'); }
--- a/jdk/src/share/classes/javax/swing/text/html/StyleSheet.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/html/StyleSheet.java Tue Nov 09 22:53:18 2010 -0800 @@ -998,7 +998,7 @@ void addRule(String[] selector, AttributeSet declaration, boolean isLinked) { int n = selector.length; - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.append(selector[0]); for (int counter = 1; counter < n; counter++) { sb.append(' ');
--- a/jdk/src/share/classes/javax/swing/text/html/parser/Parser.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/html/parser/Parser.java Tue Nov 09 22:53:18 2010 -0800 @@ -1470,7 +1470,7 @@ */ public String parseDTDMarkup() throws IOException { - StringBuffer strBuff = new StringBuffer(); + StringBuilder strBuff = new StringBuilder(); ch = readCh(); while(true) { switch (ch) {
--- a/jdk/src/share/classes/javax/swing/text/rtf/AbstractFilter.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/javax/swing/text/rtf/AbstractFilter.java Tue Nov 09 22:53:18 2010 -0800 @@ -160,7 +160,7 @@ public void write(byte[] buf, int off, int len) throws IOException { - StringBuffer accumulator = null; + StringBuilder accumulator = null; while (len > 0) { short b = (short)buf[off]; @@ -178,7 +178,7 @@ char ch = translationTable[b]; if (ch != (char)0) { if (accumulator == null) - accumulator = new StringBuffer(); + accumulator = new StringBuilder(); accumulator.append(ch); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/java2d/pisces/Curve.java Tue Nov 09 22:53:18 2010 -0800 @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2007, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.java2d.pisces; + +import java.util.Iterator; + +class Curve { + + float ax, ay, bx, by, cx, cy, dx, dy; + float dax, day, dbx, dby; + + Curve() { + } + + void set(float[] points, int type) { + switch(type) { + case 8: + set(points[0], points[1], + points[2], points[3], + points[4], points[5], + points[6], points[7]); + break; + case 6: + set(points[0], points[1], + points[2], points[3], + points[4], points[5]); + break; + default: + throw new InternalError("Curves can only be cubic or quadratic"); + } + } + + void set(float x1, float y1, + float x2, float y2, + float x3, float y3, + float x4, float y4) + { + ax = 3 * (x2 - x3) + x4 - x1; + ay = 3 * (y2 - y3) + y4 - y1; + bx = 3 * (x1 - 2 * x2 + x3); + by = 3 * (y1 - 2 * y2 + y3); + cx = 3 * (x2 - x1); + cy = 3 * (y2 - y1); + dx = x1; + dy = y1; + dax = 3 * ax; day = 3 * ay; + dbx = 2 * bx; dby = 2 * by; + } + + void set(float x1, float y1, + float x2, float y2, + float x3, float y3) + { + ax = ay = 0f; + + bx = x1 - 2 * x2 + x3; + by = y1 - 2 * y2 + y3; + cx = 2 * (x2 - x1); + cy = 2 * (y2 - y1); + dx = x1; + dy = y1; + dax = 0; day = 0; + dbx = 2 * bx; dby = 2 * by; + } + + float xat(float t) { + return t * (t * (t * ax + bx) + cx) + dx; + } + float yat(float t) { + return t * (t * (t * ay + by) + cy) + dy; + } + + float dxat(float t) { + return t * (t * dax + dbx) + cx; + } + + float dyat(float t) { + return t * (t * day + dby) + cy; + } + + private float ddxat(float t) { + return 2 * dax * t + dbx; + } + + private float ddyat(float t) { + return 2 * day * t + dby; + } + + int dxRoots(float[] roots, int off) { + return Helpers.quadraticRoots(dax, dbx, cx, roots, off); + } + + int dyRoots(float[] roots, int off) { + return Helpers.quadraticRoots(day, dby, cy, roots, off); + } + + int infPoints(float[] pts, int off) { + // inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0 + // Fortunately, this turns out to be quadratic, so there are at + // most 2 inflection points. + final float a = dax * dby - dbx * day; + final float b = 2 * (cy * dax - day * cx); + final float c = cy * dbx - cx * dby; + + return Helpers.quadraticRoots(a, b, c, pts, off); + } + + // finds points where the first and second derivative are + // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where + // * is a dot product). Unfortunately, we have to solve a cubic. + private int perpendiculardfddf(float[] pts, int off, final float err) { + assert pts.length >= off + 4; + + // these are the coefficients of g(t): + final float a = 2*(dax*dax + day*day); + final float b = 3*(dax*dbx + day*dby); + final float c = 2*(dax*cx + day*cy) + dbx*dbx + dby*dby; + final float d = dbx*cx + dby*cy; + // TODO: We might want to divide the polynomial by a to make the + // coefficients smaller. This won't change the roots. + return Helpers.cubicRootsInAB(a, b, c, d, pts, off, err, 0f, 1f); + } + + // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses + // a variant of the false position algorithm to find the roots. False + // position requires that 2 initial values x0,x1 be given, and that the + // function must have opposite signs at those values. To find such + // values, we need the local extrema of the ROC function, for which we + // need the roots of its derivative; however, it's harder to find the + // roots of the derivative in this case than it is to find the roots + // of the original function. So, we find all points where this curve's + // first and second derivative are perpendicular, and we pretend these + // are our local extrema. There are at most 3 of these, so we will check + // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection + // points, so roc-w can have at least 6 roots. This shouldn't be a + // problem for what we're trying to do (draw a nice looking curve). + int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) { + // no OOB exception, because by now off<=6, and roots.length >= 10 + assert off <= 6 && roots.length >= 10; + int ret = off; + int numPerpdfddf = perpendiculardfddf(roots, off, err); + float t0 = 0, ft0 = ROCsq(t0) - w*w; + roots[off + numPerpdfddf] = 1f; // always check interval end points + numPerpdfddf++; + for (int i = off; i < off + numPerpdfddf; i++) { + float t1 = roots[i], ft1 = ROCsq(t1) - w*w; + if (ft0 == 0f) { + roots[ret++] = t0; + } else if (ft1 * ft0 < 0f) { // have opposite signs + // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because + // ROC(t) >= 0 for all t. + roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err); + } + t0 = t1; + ft0 = ft1; + } + + return ret - off; + } + + private static float eliminateInf(float x) { + return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE : + (x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x)); + } + + // A slight modification of the false position algorithm on wikipedia. + // This only works for the ROCsq-x functions. It might be nice to have + // the function as an argument, but that would be awkward in java6. + // It is something to consider for java7, depending on how closures + // and function objects turn out. Same goes for the newton's method + // algorithm in Helpers.java + private float falsePositionROCsqMinusX(float x0, float x1, + final float x, final float err) + { + final int iterLimit = 100; + int side = 0; + float t = x1, ft = eliminateInf(ROCsq(t) - x); + float s = x0, fs = eliminateInf(ROCsq(s) - x); + float r = s, fr; + for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) { + r = (fs * t - ft * s) / (fs - ft); + fr = ROCsq(r) - x; + if (fr * ft > 0) {// have the same sign + ft = fr; t = r; + if (side < 0) { + fs /= (1 << (-side)); + side--; + } else { + side = -1; + } + } else if (fr * fs > 0) { + fs = fr; s = r; + if (side > 0) { + ft /= (1 << side); + side++; + } else { + side = 1; + } + } else { + break; + } + } + return r; + } + + // returns the radius of curvature squared at t of this curve + // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications) + private float ROCsq(final float t) { + final float dx = dxat(t); + final float dy = dyat(t); + final float ddx = ddxat(t); + final float ddy = ddyat(t); + final float dx2dy2 = dx*dx + dy*dy; + final float ddx2ddy2 = ddx*ddx + ddy*ddy; + final float ddxdxddydy = ddx*dx + ddy*dy; + float ret = ((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy))*dx2dy2; + return ret; + } + + // curve to be broken should be in pts[0] + // this will change the contents of both pts and Ts + // TODO: There's no reason for Ts to be an array. All we need is a sequence + // of t values at which to subdivide. An array statisfies this condition, + // but is unnecessarily restrictive. Ts should be an Iterator<Float> instead. + // Doing this will also make dashing easier, since we could easily make + // LengthIterator an Iterator<Float> and feed it to this function to simplify + // the loop in Dasher.somethingTo. + static Iterator<float[]> breakPtsAtTs(final float[][] pts, final int type, + final float[] Ts, final int numTs) + { + assert pts.length >= 2 && pts[0].length >= 8 && numTs <= Ts.length; + return new Iterator<float[]>() { + int nextIdx = 0; + int nextCurveIdx = 0; + float prevT = 0; + + @Override public boolean hasNext() { + return nextCurveIdx < numTs + 1; + } + + @Override public float[] next() { + float[] ret; + if (nextCurveIdx < numTs) { + float curT = Ts[nextCurveIdx]; + float splitT = (curT - prevT) / (1 - prevT); + Helpers.subdivideAt(splitT, + pts[nextIdx], 0, + pts[nextIdx], 0, + pts[1-nextIdx], 0, type); + updateTs(Ts, Ts[nextCurveIdx], nextCurveIdx + 1, numTs - nextCurveIdx - 1); + ret = pts[nextIdx]; + nextIdx = 1 - nextIdx; + } else { + ret = pts[nextIdx]; + } + nextCurveIdx++; + return ret; + } + + @Override public void remove() {} + }; + } + + // precondition: ts[off]...ts[off+len-1] must all be greater than t. + private static void updateTs(float[] ts, final float t, final int off, final int len) { + for (int i = off; i < off + len; i++) { + ts[i] = (ts[i] - t) / (1 - t); + } + } +} +
--- a/jdk/src/share/classes/sun/java2d/pisces/Dasher.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/sun/java2d/pisces/Dasher.java Tue Nov 09 22:53:18 2010 -0800 @@ -25,6 +25,8 @@ package sun.java2d.pisces; +import sun.awt.geom.PathConsumer2D; + /** * The <code>Dasher</code> class takes a series of linear commands * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and @@ -36,18 +38,16 @@ * semantics are unclear. * */ -public class Dasher implements LineSink { - private final LineSink output; +public class Dasher implements sun.awt.geom.PathConsumer2D { + + private final PathConsumer2D out; private final float[] dash; private final float startPhase; private final boolean startDashOn; private final int startIdx; - private final float m00, m10, m01, m11; - private final float det; - - private boolean firstDashOn; private boolean starting; + private boolean needsMoveTo; private int idx; private boolean dashOn; @@ -55,28 +55,23 @@ private float sx, sy; private float x0, y0; - private float sx1, sy1; + // temporary storage for the current curve + private float[] curCurvepts; /** * Constructs a <code>Dasher</code>. * - * @param output an output <code>LineSink</code>. - * @param dash an array of <code>int</code>s containing the dash pattern - * @param phase an <code>int</code> containing the dash phase - * @param transform a <code>Transform4</code> object indicating - * the transform that has been previously applied to all incoming - * coordinates. This is required in order to compute dash lengths - * properly. + * @param out an output <code>PathConsumer2D</code>. + * @param dash an array of <code>float</code>s containing the dash pattern + * @param phase a <code>float</code> containing the dash phase */ - public Dasher(LineSink output, - float[] dash, float phase, - float a00, float a01, float a10, float a11) { + public Dasher(PathConsumer2D out, float[] dash, float phase) { if (phase < 0) { throw new IllegalArgumentException("phase < 0 !"); } - this.output = output; + this.out = out; // Normalize so 0 <= phase < dash[0] int idx = 0; @@ -92,16 +87,19 @@ this.startPhase = this.phase = phase; this.startDashOn = dashOn; this.startIdx = idx; + this.starting = true; - m00 = a00; - m01 = a01; - m10 = a10; - m11 = a11; - det = m00 * m11 - m01 * m10; + // we need curCurvepts to be able to contain 2 curves because when + // dashing curves, we need to subdivide it + curCurvepts = new float[8 * 2]; } public void moveTo(float x0, float y0) { - output.moveTo(x0, y0); + if (firstSegidx > 0) { + out.moveTo(sx, sy); + emitFirstSegments(); + } + needsMoveTo = true; this.idx = startIdx; this.dashOn = this.startDashOn; this.phase = this.startPhase; @@ -110,88 +108,108 @@ this.starting = true; } - public void lineJoin() { - output.lineJoin(); + private void emitSeg(float[] buf, int off, int type) { + switch (type) { + case 8: + out.curveTo(buf[off+0], buf[off+1], + buf[off+2], buf[off+3], + buf[off+4], buf[off+5]); + break; + case 6: + out.quadTo(buf[off+0], buf[off+1], + buf[off+2], buf[off+3]); + break; + case 4: + out.lineTo(buf[off], buf[off+1]); + } } - private void goTo(float x1, float y1) { + private void emitFirstSegments() { + for (int i = 0; i < firstSegidx; ) { + emitSeg(firstSegmentsBuffer, i+1, (int)firstSegmentsBuffer[i]); + i += (((int)firstSegmentsBuffer[i]) - 1); + } + firstSegidx = 0; + } + + // We don't emit the first dash right away. If we did, caps would be + // drawn on it, but we need joins to be drawn if there's a closePath() + // So, we store the path elements that make up the first dash in the + // buffer below. + private float[] firstSegmentsBuffer = new float[7]; + private int firstSegidx = 0; + // precondition: pts must be in relative coordinates (relative to x0,y0) + // fullCurve is true iff the curve in pts has not been split. + private void goTo(float[] pts, int off, final int type) { + float x = pts[off + type - 4]; + float y = pts[off + type - 3]; if (dashOn) { if (starting) { - this.sx1 = x1; - this.sy1 = y1; - firstDashOn = true; - starting = false; + firstSegmentsBuffer = Helpers.widenArray(firstSegmentsBuffer, + firstSegidx, type - 2); + firstSegmentsBuffer[firstSegidx++] = type; + System.arraycopy(pts, off, firstSegmentsBuffer, firstSegidx, type - 2); + firstSegidx += type - 2; + } else { + if (needsMoveTo) { + out.moveTo(x0, y0); + needsMoveTo = false; + } + emitSeg(pts, off, type); } - output.lineTo(x1, y1); } else { - if (starting) { - firstDashOn = false; - starting = false; - } - output.moveTo(x1, y1); + starting = false; + needsMoveTo = true; } - this.x0 = x1; - this.y0 = y1; + this.x0 = x; + this.y0 = y; } public void lineTo(float x1, float y1) { - // The widened line is squished to a 0 width one, so no drawing is done - if (det == 0) { - goTo(x1, y1); - return; - } float dx = x1 - x0; float dy = y1 - y0; + float len = (float) Math.hypot(dx, dy); - // Compute segment length in the untransformed - // coordinate system - - float la = (dy*m00 - dx*m10)/det; - float lb = (dy*m01 - dx*m11)/det; - float origLen = (float) Math.hypot(la, lb); - - if (origLen == 0) { - // Let the output LineSink deal with cases where dx, dy are 0. - goTo(x1, y1); + if (len == 0) { return; } // The scaling factors needed to get the dx and dy of the // transformed dash segments. - float cx = dx / origLen; - float cy = dy / origLen; + float cx = dx / len; + float cy = dy / len; while (true) { float leftInThisDashSegment = dash[idx] - phase; - if (origLen < leftInThisDashSegment) { - goTo(x1, y1); + if (len <= leftInThisDashSegment) { + curCurvepts[0] = x1; + curCurvepts[1] = y1; + goTo(curCurvepts, 0, 4); // Advance phase within current dash segment - phase += origLen; - return; - } else if (origLen == leftInThisDashSegment) { - goTo(x1, y1); - phase = 0f; - idx = (idx + 1) % dash.length; - dashOn = !dashOn; + phase += len; + if (len == leftInThisDashSegment) { + phase = 0f; + idx = (idx + 1) % dash.length; + dashOn = !dashOn; + } return; } - float dashx, dashy; float dashdx = dash[idx] * cx; float dashdy = dash[idx] * cy; if (phase == 0) { - dashx = x0 + dashdx; - dashy = y0 + dashdy; + curCurvepts[0] = x0 + dashdx; + curCurvepts[1] = y0 + dashdy; } else { - float p = (leftInThisDashSegment) / dash[idx]; - dashx = x0 + p * dashdx; - dashy = y0 + p * dashdy; + float p = leftInThisDashSegment / dash[idx]; + curCurvepts[0] = x0 + p * dashdx; + curCurvepts[1] = y0 + p * dashdy; } - goTo(dashx, dashy); + goTo(curCurvepts, 0, 4); - origLen -= (dash[idx] - phase); + len -= leftInThisDashSegment; // Advance to next dash segment idx = (idx + 1) % dash.length; dashOn = !dashOn; @@ -199,15 +217,289 @@ } } + private LengthIterator li = null; - public void close() { - lineTo(sx, sy); - if (firstDashOn) { - output.lineTo(sx1, sy1); + // preconditions: curCurvepts must be an array of length at least 2 * type, + // that contains the curve we want to dash in the first type elements + private void somethingTo(int type) { + if (pointCurve(curCurvepts, type)) { + return; + } + if (li == null) { + li = new LengthIterator(4, 0.0001f); + } + li.initializeIterationOnCurve(curCurvepts, type); + + int curCurveoff = 0; // initially the current curve is at curCurvepts[0...type] + float lastSplitT = 0; + float t = 0; + float leftInThisDashSegment = dash[idx] - phase; + while ((t = li.next(leftInThisDashSegment)) < 1) { + if (t != 0) { + Helpers.subdivideAt((t - lastSplitT) / (1 - lastSplitT), + curCurvepts, curCurveoff, + curCurvepts, 0, + curCurvepts, type, type); + lastSplitT = t; + goTo(curCurvepts, 2, type); + curCurveoff = type; + } + // Advance to next dash segment + idx = (idx + 1) % dash.length; + dashOn = !dashOn; + phase = 0; + leftInThisDashSegment = dash[idx]; + } + goTo(curCurvepts, curCurveoff+2, type); + phase += li.lastSegLen(); + if (phase >= dash[idx]) { + phase = 0f; + idx = (idx + 1) % dash.length; + dashOn = !dashOn; } } - public void end() { - output.end(); + private static boolean pointCurve(float[] curve, int type) { + for (int i = 2; i < type; i++) { + if (curve[i] != curve[i-2]) { + return false; + } + } + return true; + } + + // Objects of this class are used to iterate through curves. They return + // t values where the left side of the curve has a specified length. + // It does this by subdividing the input curve until a certain error + // condition has been met. A recursive subdivision procedure would + // return as many as 1<<limit curves, but this is an iterator and we + // don't need all the curves all at once, so what we carry out a + // lazy inorder traversal of the recursion tree (meaning we only move + // through the tree when we need the next subdivided curve). This saves + // us a lot of memory because at any one time we only need to store + // limit+1 curves - one for each level of the tree + 1. + // NOTE: the way we do things here is not enough to traverse a general + // tree; however, the trees we are interested in have the property that + // every non leaf node has exactly 2 children + private static class LengthIterator { + private enum Side {LEFT, RIGHT}; + // Holds the curves at various levels of the recursion. The root + // (i.e. the original curve) is at recCurveStack[0] (but then it + // gets subdivided, the left half is put at 1, so most of the time + // only the right half of the original curve is at 0) + private float[][] recCurveStack; + // sides[i] indicates whether the node at level i+1 in the path from + // the root to the current leaf is a left or right child of its parent. + private Side[] sides; + private int curveType; + private final int limit; + private final float ERR; + private final float minTincrement; + // lastT and nextT delimit the current leaf. + private float nextT; + private float lenAtNextT; + private float lastT; + private float lenAtLastT; + private float lenAtLastSplit; + private float lastSegLen; + // the current level in the recursion tree. 0 is the root. limit + // is the deepest possible leaf. + private int recLevel; + private boolean done; + + public LengthIterator(int reclimit, float err) { + this.limit = reclimit; + this.minTincrement = 1f / (1 << limit); + this.ERR = err; + this.recCurveStack = new float[reclimit+1][8]; + this.sides = new Side[reclimit]; + // if any methods are called without first initializing this object on + // a curve, we want it to fail ASAP. + this.nextT = Float.MAX_VALUE; + this.lenAtNextT = Float.MAX_VALUE; + this.lenAtLastSplit = Float.MIN_VALUE; + this.recLevel = Integer.MIN_VALUE; + this.lastSegLen = Float.MAX_VALUE; + this.done = true; + } + + public void initializeIterationOnCurve(float[] pts, int type) { + System.arraycopy(pts, 0, recCurveStack[0], 0, type); + this.curveType = type; + this.recLevel = 0; + this.lastT = 0; + this.lenAtLastT = 0; + this.nextT = 0; + this.lenAtNextT = 0; + goLeft(); // initializes nextT and lenAtNextT properly + this.lenAtLastSplit = 0; + if (recLevel > 0) { + this.sides[0] = Side.LEFT; + this.done = false; + } else { + // the root of the tree is a leaf so we're done. + this.sides[0] = Side.RIGHT; + this.done = true; + } + this.lastSegLen = 0; + } + + // returns the t value where the remaining curve should be split in + // order for the left subdivided curve to have length len. If len + // is >= than the length of the uniterated curve, it returns 1. + public float next(float len) { + float targetLength = lenAtLastSplit + len; + while(lenAtNextT < targetLength) { + if (done) { + lastSegLen = lenAtNextT - lenAtLastSplit; + return 1; + } + goToNextLeaf(); + } + lenAtLastSplit = targetLength; + float t = binSearchForLen(lenAtLastSplit - lenAtLastT, + recCurveStack[recLevel], curveType, lenAtNextT - lenAtLastT, ERR); + // t is relative to the current leaf, so we must make it a valid parameter + // of the original curve. + t = t * (nextT - lastT) + lastT; + if (t >= 1) { + t = 1; + done = true; + } + // even if done = true, if we're here, that means targetLength + // is equal to, or very, very close to the total length of the + // curve, so lastSegLen won't be too high. In cases where len + // overshoots the curve, this method will exit in the while + // loop, and lastSegLen will still be set to the right value. + lastSegLen = len; + return t; + } + + public float lastSegLen() { + return lastSegLen; + } + + // Returns t such that if leaf is subdivided at t the left + // curve will have length len. leafLen must be the length of leaf. + private static Curve bsc = new Curve(); + private static float binSearchForLen(float len, float[] leaf, int type, + float leafLen, float err) + { + assert len <= leafLen; + bsc.set(leaf, type); + float errBound = err*len; + float left = 0, right = 1; + while (left < right) { + float m = (left + right) / 2; + if (m == left || m == right) { + return m; + } + float x = bsc.xat(m); + float y = bsc.yat(m); + float leftLen = Helpers.linelen(leaf[0], leaf[1], x, y); + if (Math.abs(leftLen - len) < errBound) { + return m; + } + if (leftLen < len) { + left = m; + } else { + right = m; + } + } + return left; + } + + // go to the next leaf (in an inorder traversal) in the recursion tree + // preconditions: must be on a leaf, and that leaf must not be the root. + private void goToNextLeaf() { + // We must go to the first ancestor node that has an unvisited + // right child. + recLevel--; + while(sides[recLevel] == Side.RIGHT) { + if (recLevel == 0) { + done = true; + return; + } + recLevel--; + } + + sides[recLevel] = Side.RIGHT; + System.arraycopy(recCurveStack[recLevel], 0, recCurveStack[recLevel+1], 0, curveType); + recLevel++; + goLeft(); + } + + // go to the leftmost node from the current node. Return its length. + private void goLeft() { + float len = onLeaf(); + if (len >= 0) { + lastT = nextT; + lenAtLastT = lenAtNextT; + nextT += (1 << (limit - recLevel)) * minTincrement; + lenAtNextT += len; + } else { + Helpers.subdivide(recCurveStack[recLevel], 0, + recCurveStack[recLevel+1], 0, + recCurveStack[recLevel], 0, curveType); + sides[recLevel] = Side.LEFT; + recLevel++; + goLeft(); + } + } + + // this is a bit of a hack. It returns -1 if we're not on a leaf, and + // the length of the leaf if we are on a leaf. + private float onLeaf() { + float polylen = Helpers.polyLineLength(recCurveStack[recLevel], 0, curveType); + float linelen = Helpers.linelen(recCurveStack[recLevel][0], recCurveStack[recLevel][1], + recCurveStack[recLevel][curveType - 2], recCurveStack[recLevel][curveType - 1]); + return (polylen - linelen < ERR || recLevel == limit) ? + (polylen + linelen)/2 : -1; + } + } + + @Override + public void curveTo(float x1, float y1, + float x2, float y2, + float x3, float y3) + { + curCurvepts[0] = x0; curCurvepts[1] = y0; + curCurvepts[2] = x1; curCurvepts[3] = y1; + curCurvepts[4] = x2; curCurvepts[5] = y2; + curCurvepts[6] = x3; curCurvepts[7] = y3; + somethingTo(8); + } + + @Override + public void quadTo(float x1, float y1, float x2, float y2) { + curCurvepts[0] = x0; curCurvepts[1] = y0; + curCurvepts[2] = x1; curCurvepts[3] = y1; + curCurvepts[4] = x2; curCurvepts[5] = y2; + somethingTo(6); + } + + public void closePath() { + lineTo(sx, sy); + if (firstSegidx > 0) { + if (!dashOn || needsMoveTo) { + out.moveTo(sx, sy); + } + emitFirstSegments(); + } + moveTo(sx, sy); + } + + public void pathDone() { + if (firstSegidx > 0) { + out.moveTo(sx, sy); + emitFirstSegments(); + } + out.pathDone(); + } + + @Override + public long getNativeConsumer() { + throw new InternalError("Dasher does not use a native consumer"); } } +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/java2d/pisces/Helpers.java Tue Nov 09 22:53:18 2010 -0800 @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2007, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.java2d.pisces; + +import java.util.Arrays; + +final class Helpers { + private Helpers() { + throw new Error("This is a non instantiable class"); + } + + static boolean within(final float x, final float y, final float err) { + final float d = y - x; + return (d <= err && d >= -err); + } + + static boolean within(final double x, final double y, final double err) { + final double d = y - x; + return (d <= err && d >= -err); + } + + static int quadraticRoots(final float a, final float b, + final float c, float[] zeroes, final int off) + { + int ret = off; + float t; + if (a != 0f) { + final float dis = b*b - 4*a*c; + if (dis > 0) { + final float sqrtDis = (float)Math.sqrt(dis); + // depending on the sign of b we use a slightly different + // algorithm than the traditional one to find one of the roots + // so we can avoid adding numbers of different signs (which + // might result in loss of precision). + if (b >= 0) { + zeroes[ret++] = (2 * c) / (-b - sqrtDis); + zeroes[ret++] = (-b - sqrtDis) / (2 * a); + } else { + zeroes[ret++] = (-b + sqrtDis) / (2 * a); + zeroes[ret++] = (2 * c) / (-b + sqrtDis); + } + } else if (dis == 0f) { + t = (-b) / (2 * a); + zeroes[ret++] = t; + } + } else { + if (b != 0f) { + t = (-c) / b; + zeroes[ret++] = t; + } + } + return ret - off; + } + + // find the roots of g(t) = a*t^3 + b*t^2 + c*t + d in [A,B) + // We will not use Cardano's method, since it is complicated and + // involves too many square and cubic roots. We will use Newton's method. + // TODO: this should probably return ALL roots. Then the user can do + // his own filtering of roots outside [A,B). + static int cubicRootsInAB(final float a, final float b, + final float c, final float d, + float[] pts, final int off, final float E, + final float A, final float B) + { + if (a == 0) { + return quadraticRoots(b, c, d, pts, off); + } + // the coefficients of g'(t). no dc variable because dc=c + // we use these to get the critical points of g(t), which + // we then use to chose starting points for Newton's method. These + // should be very close to the actual roots. + final float da = 3 * a; + final float db = 2 * b; + int numCritPts = quadraticRoots(da, db, c, pts, off+1); + numCritPts = filterOutNotInAB(pts, off+1, numCritPts, A, B) - off - 1; + // need them sorted. + if (numCritPts == 2 && pts[off+1] > pts[off+2]) { + float tmp = pts[off+1]; + pts[off+1] = pts[off+2]; + pts[off+2] = tmp; + } + + int ret = off; + + // we don't actually care much about the extrema themselves. We + // only use them to ensure that g(t) is monotonic in each + // interval [pts[i],pts[i+1] (for i in off...off+numCritPts+1). + // This will allow us to determine intervals containing exactly + // one root. + // The end points of the interval are always local extrema. + pts[off] = A; + pts[off + numCritPts + 1] = B; + numCritPts += 2; + + float x0 = pts[off], fx0 = evalCubic(a, b, c, d, x0); + for (int i = off; i < off + numCritPts - 1; i++) { + float x1 = pts[i+1], fx1 = evalCubic(a, b, c, d, x1); + if (fx0 == 0f) { + pts[ret++] = x0; + } else if (fx1 * fx0 < 0f) { // have opposite signs + pts[ret++] = CubicNewton(a, b, c, d, + x0 + fx0 * (x1 - x0) / (fx0 - fx1), E); + } + x0 = x1; + fx0 = fx1; + } + return ret - off; + } + + // precondition: the polynomial to be evaluated must not be 0 at x0. + static float CubicNewton(final float a, final float b, + final float c, final float d, + float x0, final float err) + { + // considering how this function is used, 10 should be more than enough + final int itlimit = 10; + float fx0 = evalCubic(a, b, c, d, x0); + float x1; + int count = 0; + while(true) { + x1 = x0 - (fx0 / evalCubic(0, 3 * a, 2 * b, c, x0)); + if (Math.abs(x1 - x0) < err * Math.abs(x1 + x0) || count == itlimit) { + break; + } + x0 = x1; + fx0 = evalCubic(a, b, c, d, x0); + count++; + } + return x1; + } + + // fills the input array with numbers 0, INC, 2*INC, ... + static void fillWithIdxes(final float[] data, final int[] idxes) { + if (idxes.length > 0) { + idxes[0] = 0; + for (int i = 1; i < idxes.length; i++) { + idxes[i] = idxes[i-1] + (int)data[idxes[i-1]]; + } + } + } + + static void fillWithIdxes(final int[] idxes, final int inc) { + if (idxes.length > 0) { + idxes[0] = 0; + for (int i = 1; i < idxes.length; i++) { + idxes[i] = idxes[i-1] + inc; + } + } + } + + // These use a hardcoded factor of 2 for increasing sizes. Perhaps this + // should be provided as an argument. + static float[] widenArray(float[] in, final int cursize, final int numToAdd) { + if (in == null) { + return new float[5 * numToAdd]; + } + if (in.length >= cursize + numToAdd) { + return in; + } + return Arrays.copyOf(in, 2 * (cursize + numToAdd)); + } + static int[] widenArray(int[] in, final int cursize, final int numToAdd) { + if (in.length >= cursize + numToAdd) { + return in; + } + return Arrays.copyOf(in, 2 * (cursize + numToAdd)); + } + + static float evalCubic(final float a, final float b, + final float c, final float d, + final float t) + { + return t * (t * (t * a + b) + c) + d; + } + + static float evalQuad(final float a, final float b, + final float c, final float t) + { + return t * (t * a + b) + c; + } + + // returns the index 1 past the last valid element remaining after filtering + static int filterOutNotInAB(float[] nums, final int off, final int len, + final float a, final float b) + { + int ret = off; + for (int i = off; i < off + len; i++) { + if (nums[i] > a && nums[i] < b) { + nums[ret++] = nums[i]; + } + } + return ret; + } + + static float polyLineLength(float[] poly, final int off, final int nCoords) { + assert nCoords % 2 == 0 && poly.length >= off + nCoords : ""; + float acc = 0; + for (int i = off + 2; i < off + nCoords; i += 2) { + acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]); + } + return acc; + } + + static float linelen(float x1, float y1, float x2, float y2) { + return (float)Math.hypot(x2 - x1, y2 - y1); + } + + static void subdivide(float[] src, int srcoff, float[] left, int leftoff, + float[] right, int rightoff, int type) + { + switch(type) { + case 6: + Helpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff); + break; + case 8: + Helpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff); + break; + default: + throw new InternalError("Unsupported curve type"); + } + } + + static void isort(float[] a, int off, int len) { + for (int i = off + 1; i < off + len; i++) { + float ai = a[i]; + int j = i - 1; + for (; j >= off && a[j] > ai; j--) { + a[j+1] = a[j]; + } + a[j+1] = ai; + } + } + + // Most of these are copied from classes in java.awt.geom because we need + // float versions of these functions, and Line2D, CubicCurve2D, + // QuadCurve2D don't provide them. + /** + * Subdivides the cubic curve specified by the coordinates + * stored in the <code>src</code> array at indices <code>srcoff</code> + * through (<code>srcoff</code> + 7) and stores the + * resulting two subdivided curves into the two result arrays at the + * corresponding indices. + * Either or both of the <code>left</code> and <code>right</code> + * arrays may be <code>null</code> or a reference to the same array + * as the <code>src</code> array. + * Note that the last point in the first subdivided curve is the + * same as the first point in the second subdivided curve. Thus, + * it is possible to pass the same array for <code>left</code> + * and <code>right</code> and to use offsets, such as <code>rightoff</code> + * equals (<code>leftoff</code> + 6), in order + * to avoid allocating extra storage for this common point. + * @param src the array holding the coordinates for the source curve + * @param srcoff the offset into the array of the beginning of the + * the 6 source coordinates + * @param left the array for storing the coordinates for the first + * half of the subdivided curve + * @param leftoff the offset into the array of the beginning of the + * the 6 left coordinates + * @param right the array for storing the coordinates for the second + * half of the subdivided curve + * @param rightoff the offset into the array of the beginning of the + * the 6 right coordinates + * @since 1.7 + */ + static void subdivideCubic(float src[], int srcoff, + float left[], int leftoff, + float right[], int rightoff) + { + float x1 = src[srcoff + 0]; + float y1 = src[srcoff + 1]; + float ctrlx1 = src[srcoff + 2]; + float ctrly1 = src[srcoff + 3]; + float ctrlx2 = src[srcoff + 4]; + float ctrly2 = src[srcoff + 5]; + float x2 = src[srcoff + 6]; + float y2 = src[srcoff + 7]; + if (left != null) { + left[leftoff + 0] = x1; + left[leftoff + 1] = y1; + } + if (right != null) { + right[rightoff + 6] = x2; + right[rightoff + 7] = y2; + } + x1 = (x1 + ctrlx1) / 2.0f; + y1 = (y1 + ctrly1) / 2.0f; + x2 = (x2 + ctrlx2) / 2.0f; + y2 = (y2 + ctrly2) / 2.0f; + float centerx = (ctrlx1 + ctrlx2) / 2.0f; + float centery = (ctrly1 + ctrly2) / 2.0f; + ctrlx1 = (x1 + centerx) / 2.0f; + ctrly1 = (y1 + centery) / 2.0f; + ctrlx2 = (x2 + centerx) / 2.0f; + ctrly2 = (y2 + centery) / 2.0f; + centerx = (ctrlx1 + ctrlx2) / 2.0f; + centery = (ctrly1 + ctrly2) / 2.0f; + if (left != null) { + left[leftoff + 2] = x1; + left[leftoff + 3] = y1; + left[leftoff + 4] = ctrlx1; + left[leftoff + 5] = ctrly1; + left[leftoff + 6] = centerx; + left[leftoff + 7] = centery; + } + if (right != null) { + right[rightoff + 0] = centerx; + right[rightoff + 1] = centery; + right[rightoff + 2] = ctrlx2; + right[rightoff + 3] = ctrly2; + right[rightoff + 4] = x2; + right[rightoff + 5] = y2; + } + } + + + static void subdivideCubicAt(float t, float src[], int srcoff, + float left[], int leftoff, + float right[], int rightoff) + { + float x1 = src[srcoff + 0]; + float y1 = src[srcoff + 1]; + float ctrlx1 = src[srcoff + 2]; + float ctrly1 = src[srcoff + 3]; + float ctrlx2 = src[srcoff + 4]; + float ctrly2 = src[srcoff + 5]; + float x2 = src[srcoff + 6]; + float y2 = src[srcoff + 7]; + if (left != null) { + left[leftoff + 0] = x1; + left[leftoff + 1] = y1; + } + if (right != null) { + right[rightoff + 6] = x2; + right[rightoff + 7] = y2; + } + x1 = x1 + t * (ctrlx1 - x1); + y1 = y1 + t * (ctrly1 - y1); + x2 = ctrlx2 + t * (x2 - ctrlx2); + y2 = ctrly2 + t * (y2 - ctrly2); + float centerx = ctrlx1 + t * (ctrlx2 - ctrlx1); + float centery = ctrly1 + t * (ctrly2 - ctrly1); + ctrlx1 = x1 + t * (centerx - x1); + ctrly1 = y1 + t * (centery - y1); + ctrlx2 = centerx + t * (x2 - centerx); + ctrly2 = centery + t * (y2 - centery); + centerx = ctrlx1 + t * (ctrlx2 - ctrlx1); + centery = ctrly1 + t * (ctrly2 - ctrly1); + if (left != null) { + left[leftoff + 2] = x1; + left[leftoff + 3] = y1; + left[leftoff + 4] = ctrlx1; + left[leftoff + 5] = ctrly1; + left[leftoff + 6] = centerx; + left[leftoff + 7] = centery; + } + if (right != null) { + right[rightoff + 0] = centerx; + right[rightoff + 1] = centery; + right[rightoff + 2] = ctrlx2; + right[rightoff + 3] = ctrly2; + right[rightoff + 4] = x2; + right[rightoff + 5] = y2; + } + } + + static void subdivideQuad(float src[], int srcoff, + float left[], int leftoff, + float right[], int rightoff) + { + float x1 = src[srcoff + 0]; + float y1 = src[srcoff + 1]; + float ctrlx = src[srcoff + 2]; + float ctrly = src[srcoff + 3]; + float x2 = src[srcoff + 4]; + float y2 = src[srcoff + 5]; + if (left != null) { + left[leftoff + 0] = x1; + left[leftoff + 1] = y1; + } + if (right != null) { + right[rightoff + 4] = x2; + right[rightoff + 5] = y2; + } + x1 = (x1 + ctrlx) / 2.0f; + y1 = (y1 + ctrly) / 2.0f; + x2 = (x2 + ctrlx) / 2.0f; + y2 = (y2 + ctrly) / 2.0f; + ctrlx = (x1 + x2) / 2.0f; + ctrly = (y1 + y2) / 2.0f; + if (left != null) { + left[leftoff + 2] = x1; + left[leftoff + 3] = y1; + left[leftoff + 4] = ctrlx; + left[leftoff + 5] = ctrly; + } + if (right != null) { + right[rightoff + 0] = ctrlx; + right[rightoff + 1] = ctrly; + right[rightoff + 2] = x2; + right[rightoff + 3] = y2; + } + } + + static void subdivideQuadAt(float t, float src[], int srcoff, + float left[], int leftoff, + float right[], int rightoff) + { + float x1 = src[srcoff + 0]; + float y1 = src[srcoff + 1]; + float ctrlx = src[srcoff + 2]; + float ctrly = src[srcoff + 3]; + float x2 = src[srcoff + 4]; + float y2 = src[srcoff + 5]; + if (left != null) { + left[leftoff + 0] = x1; + left[leftoff + 1] = y1; + } + if (right != null) { + right[rightoff + 4] = x2; + right[rightoff + 5] = y2; + } + x1 = x1 + t * (ctrlx - x1); + y1 = y1 + t * (ctrly - y1); + x2 = ctrlx + t * (x2 - ctrlx); + y2 = ctrly + t * (y2 - ctrly); + ctrlx = x1 + t * (x2 - x1); + ctrly = y1 + t * (y2 - y1); + if (left != null) { + left[leftoff + 2] = x1; + left[leftoff + 3] = y1; + left[leftoff + 4] = ctrlx; + left[leftoff + 5] = ctrly; + } + if (right != null) { + right[rightoff + 0] = ctrlx; + right[rightoff + 1] = ctrly; + right[rightoff + 2] = x2; + right[rightoff + 3] = y2; + } + } + + static void subdivideAt(float t, float src[], int srcoff, + float left[], int leftoff, + float right[], int rightoff, int size) + { + switch(size) { + case 8: + subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff); + break; + case 6: + subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff); + break; + } + } +}
--- a/jdk/src/share/classes/sun/java2d/pisces/LineSink.java Tue Nov 09 11:45:00 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2007, Oracle and/or its affiliates. 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.java2d.pisces; - -/** - * The <code>LineSink</code> interface accepts a series of line - * drawing commands: <code>moveTo</code>, <code>lineTo</code>, - * <code>close</code> (equivalent to a <code>lineTo</code> command - * with an argument equal to the argument of the last - * <code>moveTo</code> command), and <code>end</code>. - * - * <p> A <code>Flattener</code> may be used to connect a general path - * source to a <code>LineSink</code>. - * - * <p> The <code>Renderer</code> class implements the - * <code>LineSink</code> interface. - * - */ -public interface LineSink { - - /** - * Moves the current drawing position to the point <code>(x0, - * y0)</code>. - * - * @param x0 the X coordinate - * @param y0 the Y coordinate - */ - public void moveTo(float x0, float y0); - - /** - * Provides a hint that the current segment should be joined to - * the following segment using an explicit miter or round join if - * required. - * - * <p> An application-generated path will generally have no need - * to contain calls to this method; they are typically introduced - * by a <code>Flattener</code> to mark segment divisions that - * appear in its input, and consumed by a <code>Stroker</code> - * that is responsible for emitting the miter or round join - * segments. - * - * <p> Other <code>LineSink</code> classes should simply pass this - * hint to their output sink as needed. - */ - public void lineJoin(); - - /** - * Draws a line from the current drawing position to the point - * <code>(x1, y1)</code> and sets the current drawing position to - * <code>(x1, y1)</code>. - * - * @param x1 the X coordinate - * @param y1 the Y coordinate - */ - public void lineTo(float x1, float y1); - - /** - * Closes the current path by drawing a line from the current - * drawing position to the point specified by the moset recent - * <code>moveTo</code> command. - */ - public void close(); - - /** - * Ends the current path. It may be necessary to end a path in - * order to allow end caps to be drawn. - */ - public void end(); - -}
--- a/jdk/src/share/classes/sun/java2d/pisces/PiscesCache.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/sun/java2d/pisces/PiscesCache.java Tue Nov 09 22:53:18 2010 -0800 @@ -25,6 +25,8 @@ package sun.java2d.pisces; +import java.util.Arrays; + /** * An object used to cache pre-rendered complex paths. * @@ -32,115 +34,153 @@ */ public final class PiscesCache { - int bboxX0, bboxY0, bboxX1, bboxY1; + final int bboxX0, bboxY0, bboxX1, bboxY1; - byte[] rowAARLE; - int alphaRLELength; + // rowAARLE[i] holds the encoding of the pixel row with y = bboxY0+i. + // The format of each of the inner arrays is: rowAARLE[i][0,1] = (x0, n) + // where x0 is the first x in row i with nonzero alpha, and n is the + // number of RLE entries in this row. rowAARLE[i][j,j+1] for j>1 is + // (val,runlen) + final int[][] rowAARLE; - int[] rowOffsetsRLE; - int[] minTouched; - int alphaRows; + // RLE encodings are added in increasing y rows and then in increasing + // x inside those rows. Therefore, at any one time there is a well + // defined position (x,y) where a run length is about to be added (or + // the row terminated). x0,y0 is this (x,y)-(bboxX0,bboxY0). They + // are used to get indices into the current tile. + private int x0 = Integer.MIN_VALUE, y0 = Integer.MIN_VALUE; - private PiscesCache() {} + // touchedTile[i][j] is the sum of all the alphas in the tile with + // y=i*TILE_SIZE+bboxY0 and x=j*TILE_SIZE+bboxX0. + private final int[][] touchedTile; - public static PiscesCache createInstance() { - return new PiscesCache(); + static final int TILE_SIZE_LG = 5; + static final int TILE_SIZE = 1 << TILE_SIZE_LG; // 32 + private static final int INIT_ROW_SIZE = 8; // enough for 3 run lengths + + PiscesCache(int minx, int miny, int maxx, int maxy) { + assert maxy >= miny && maxx >= minx; + bboxX0 = minx; + bboxY0 = miny; + bboxX1 = maxx + 1; + bboxY1 = maxy + 1; + // we could just leave the inner arrays as null and allocate them + // lazily (which would be beneficial for shapes with gaps), but we + // assume there won't be too many of those so we allocate everything + // up front (which is better for other cases) + rowAARLE = new int[bboxY1 - bboxY0 + 1][INIT_ROW_SIZE]; + x0 = 0; + y0 = -1; // -1 makes the first assert in startRow succeed + // the ceiling of (maxy - miny + 1) / TILE_SIZE; + int nyTiles = (maxy - miny + TILE_SIZE) >> TILE_SIZE_LG; + int nxTiles = (maxx - minx + TILE_SIZE) >> TILE_SIZE_LG; + + touchedTile = new int[nyTiles][nxTiles]; } - private static final float ROWAA_RLE_FACTOR = 1.5f; - private static final float TOUCHED_FACTOR = 1.5f; - private static final int MIN_TOUCHED_LEN = 64; - - private void reallocRowAARLE(int newLength) { - if (rowAARLE == null) { - rowAARLE = new byte[newLength]; - } else if (rowAARLE.length < newLength) { - int len = Math.max(newLength, - (int)(rowAARLE.length*ROWAA_RLE_FACTOR)); - byte[] newRowAARLE = new byte[len]; - System.arraycopy(rowAARLE, 0, newRowAARLE, 0, rowAARLE.length); - rowAARLE = newRowAARLE; + void addRLERun(int val, int runLen) { + if (runLen > 0) { + addTupleToRow(y0, val, runLen); + if (val != 0) { + // the x and y of the current row, minus bboxX0, bboxY0 + int tx = x0 >> TILE_SIZE_LG; + int ty = y0 >> TILE_SIZE_LG; + int tx1 = (x0 + runLen - 1) >> TILE_SIZE_LG; + // while we forbid rows from starting before bboxx0, our users + // can still store rows that go beyond bboxx1 (although this + // shouldn't happen), so it's a good idea to check that i + // is not going out of bounds in touchedTile[ty] + if (tx1 >= touchedTile[ty].length) { + tx1 = touchedTile[ty].length - 1; + } + if (tx <= tx1) { + int nextTileXCoord = (tx + 1) << TILE_SIZE_LG; + if (nextTileXCoord > x0+runLen) { + touchedTile[ty][tx] += val * runLen; + } else { + touchedTile[ty][tx] += val * (nextTileXCoord - x0); + } + tx++; + } + // don't go all the way to tx1 - we need to handle the last + // tile as a special case (just like we did with the first + for (; tx < tx1; tx++) { +// try { + touchedTile[ty][tx] += (val << TILE_SIZE_LG); +// } catch (RuntimeException e) { +// System.out.println("x0, y0: " + x0 + ", " + y0); +// System.out.printf("tx, ty, tx1: %d, %d, %d %n", tx, ty, tx1); +// System.out.printf("bboxX/Y0/1: %d, %d, %d, %d %n", +// bboxX0, bboxY0, bboxX1, bboxY1); +// throw e; +// } + } + // they will be equal unless x0>>TILE_SIZE_LG == tx1 + if (tx == tx1) { + int lastXCoord = Math.min(x0 + runLen, (tx + 1) << TILE_SIZE_LG); + int txXCoord = tx << TILE_SIZE_LG; + touchedTile[ty][tx] += val * (lastXCoord - txXCoord); + } + } + x0 += runLen; } } - private void reallocRowInfo(int newHeight) { - if (minTouched == null) { - int len = Math.max(newHeight, MIN_TOUCHED_LEN); - minTouched = new int[len]; - rowOffsetsRLE = new int[len]; - } else if (minTouched.length < newHeight) { - int len = Math.max(newHeight, - (int)(minTouched.length*TOUCHED_FACTOR)); - int[] newMinTouched = new int[len]; - int[] newRowOffsetsRLE = new int[len]; - System.arraycopy(minTouched, 0, newMinTouched, 0, - alphaRows); - System.arraycopy(rowOffsetsRLE, 0, newRowOffsetsRLE, 0, - alphaRows); - minTouched = newMinTouched; - rowOffsetsRLE = newRowOffsetsRLE; - } + void startRow(int y, int x) { + // rows are supposed to be added by increasing y. + assert y - bboxY0 > y0; + assert y <= bboxY1; // perhaps this should be < instead of <= + + y0 = y - bboxY0; + // this should be a new, uninitialized row. + assert rowAARLE[y0][1] == 0; + + x0 = x - bboxX0; + assert x0 >= 0 : "Input must not be to the left of bbox bounds"; + + // the way addTupleToRow is implemented it would work for this but it's + // not a good idea to use it because it is meant for adding + // RLE tuples, not the first tuple (which is special). + rowAARLE[y0][0] = x; + rowAARLE[y0][1] = 2; } - void addRLERun(byte val, int runLen) { - reallocRowAARLE(alphaRLELength + 2); - rowAARLE[alphaRLELength++] = val; - rowAARLE[alphaRLELength++] = (byte)runLen; + int alphaSumInTile(int x, int y) { + x -= bboxX0; + y -= bboxY0; + return touchedTile[y>>TILE_SIZE_LG][x>>TILE_SIZE_LG]; } - void startRow(int y, int x0, int x1) { - if (alphaRows == 0) { - bboxY0 = y; - bboxY1 = y+1; - bboxX0 = x0; - bboxX1 = x1+1; - } else { - if (bboxX0 > x0) bboxX0 = x0; - if (bboxX1 < x1 + 1) bboxX1 = x1 + 1; - while (bboxY1++ < y) { - reallocRowInfo(alphaRows+1); - minTouched[alphaRows] = 0; - // Assuming last 2 entries in rowAARLE are 0,0 - rowOffsetsRLE[alphaRows] = alphaRLELength-2; - alphaRows++; + int minTouched(int rowidx) { + return rowAARLE[rowidx][0]; + } + + int rowLength(int rowidx) { + return rowAARLE[rowidx][1]; + } + + private void addTupleToRow(int row, int a, int b) { + int end = rowAARLE[row][1]; + rowAARLE[row] = Helpers.widenArray(rowAARLE[row], end, 2); + rowAARLE[row][end++] = a; + rowAARLE[row][end++] = b; + rowAARLE[row][1] = end; + } + + @Override + public String toString() { + String ret = "bbox = ["+ + bboxX0+", "+bboxY0+" => "+ + bboxX1+", "+bboxY1+"]\n"; + for (int[] row : rowAARLE) { + if (row != null) { + ret += ("minTouchedX=" + row[0] + + "\tRLE Entries: " + Arrays.toString( + Arrays.copyOfRange(row, 2, row[1])) + "\n"); + } else { + ret += "[]\n"; } } - reallocRowInfo(alphaRows+1); - minTouched[alphaRows] = x0; - rowOffsetsRLE[alphaRows] = alphaRLELength; - alphaRows++; - } - - public synchronized void dispose() { - rowAARLE = null; - alphaRLELength = 0; - - minTouched = null; - rowOffsetsRLE = null; - alphaRows = 0; - - bboxX0 = bboxY0 = bboxX1 = bboxY1 = 0; - } - - public void print(java.io.PrintStream out) { - synchronized (out) { - out.println("bbox = ["+ - bboxX0+", "+bboxY0+" => "+ - bboxX1+", "+bboxY1+"]"); - - out.println("alphRLELength = "+alphaRLELength); - - for (int y = bboxY0; y < bboxY1; y++) { - int i = y-bboxY0; - out.println("row["+i+"] == {"+ - "minX = "+minTouched[i]+ - ", off = "+rowOffsetsRLE[i]+"}"); - } - - for (int i = 0; i < alphaRLELength; i += 2) { - out.println("rle["+i+"] = "+ - (rowAARLE[i+1]&0xff)+" of "+(rowAARLE[i]&0xff)); - } - } + return ret; } }
--- a/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java Tue Nov 09 22:53:18 2010 -0800 @@ -27,7 +27,7 @@ import java.awt.Shape; import java.awt.BasicStroke; -import java.awt.geom.FlatteningPathIterator; +import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Path2D; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; @@ -38,8 +38,6 @@ import sun.java2d.pipe.AATileGenerator; public class PiscesRenderingEngine extends RenderingEngine { - public static double defaultFlat = 0.1; - private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA} /** @@ -78,20 +76,29 @@ miterlimit, dashes, dashphase, - new LineSink() { + new PathConsumer2D() { public void moveTo(float x0, float y0) { p2d.moveTo(x0, y0); } - public void lineJoin() {} public void lineTo(float x1, float y1) { p2d.lineTo(x1, y1); } - public void close() { + public void closePath() { p2d.closePath(); } - public void end() {} + public void pathDone() {} + public void curveTo(float x1, float y1, + float x2, float y2, + float x3, float y3) { + p2d.curveTo(x1, y1, x2, y2, x3, y3); + } + public void quadTo(float x1, float y1, float x2, float y2) { + p2d.quadTo(x1, y1, x2, y2); + } + public long getNativeConsumer() { + throw new InternalError("Not using a native peer"); + } }); - return p2d; } @@ -133,22 +140,7 @@ NormMode norm = (normalize) ? ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA) : NormMode.OFF; - strokeTo(src, at, bs, thin, norm, antialias, - new LineSink() { - public void moveTo(float x0, float y0) { - consumer.moveTo(x0, y0); - } - public void lineJoin() {} - public void lineTo(float x1, float y1) { - consumer.lineTo(x1, y1); - } - public void close() { - consumer.closePath(); - } - public void end() { - consumer.pathDone(); - } - }); + strokeTo(src, at, bs, thin, norm, antialias, consumer); } void strokeTo(Shape src, @@ -157,7 +149,7 @@ boolean thin, NormMode normalize, boolean antialias, - LineSink lsink) + PathConsumer2D pc2d) { float lw; if (thin) { @@ -178,7 +170,7 @@ bs.getMiterLimit(), bs.getDashArray(), bs.getDashPhase(), - lsink); + pc2d); } private float userSpaceLineWidth(AffineTransform at, float lw) { @@ -256,28 +248,113 @@ float miterlimit, float dashes[], float dashphase, - LineSink lsink) + PathConsumer2D pc2d) { - float a00 = 1f, a01 = 0f, a10 = 0f, a11 = 1f; + // We use inat and outat so that in Stroker and Dasher we can work only + // with the pre-transformation coordinates. This will repeat a lot of + // computations done in the path iterator, but the alternative is to + // work with transformed paths and compute untransformed coordinates + // as needed. This would be faster but I do not think the complexity + // of working with both untransformed and transformed coordinates in + // the same code is worth it. + // However, if a path's width is constant after a transformation, + // we can skip all this untransforming. + + // If normalization is off we save some transformations by not + // transforming the input to pisces. Instead, we apply the + // transformation after the path processing has been done. + // We can't do this if normalization is on, because it isn't a good + // idea to normalize before the transformation is applied. + AffineTransform inat = null; + AffineTransform outat = null; + + PathIterator pi = null; + if (at != null && !at.isIdentity()) { - a00 = (float)at.getScaleX(); - a01 = (float)at.getShearX(); - a10 = (float)at.getShearY(); - a11 = (float)at.getScaleY(); + final double a = at.getScaleX(); + final double b = at.getShearX(); + final double c = at.getShearY(); + final double d = at.getScaleY(); + final double det = a * d - c * b; + if (Math.abs(det) <= 2 * Float.MIN_VALUE) { + // this rendering engine takes one dimensional curves and turns + // them into 2D shapes by giving them width. + // However, if everything is to be passed through a singular + // transformation, these 2D shapes will be squashed down to 1D + // again so, nothing can be drawn. + + // Every path needs an initial moveTo and a pathDone. If these + // aren't there this causes a SIGSEV in libawt.so (at the time + // of writing of this comment (September 16, 2010)). Actually, + // I'm not sure if the moveTo is necessary to avoid the SIGSEV + // but the pathDone is definitely needed. + pc2d.moveTo(0, 0); + pc2d.pathDone(); + return; + } + + // If the transform is a constant multiple of an orthogonal transformation + // then every length is just multiplied by a constant, so we just + // need to transform input paths to stroker and tell stroker + // the scaled width. This condition is satisfied if + // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we + // leave a bit of room for error. + if (nearZero(a*b + c*d, 2) && nearZero(a*a+c*c - (b*b+d*d), 2)) { + double scale = Math.sqrt(a*a + c*c); + if (dashes != null) { + dashes = java.util.Arrays.copyOf(dashes, dashes.length); + for (int i = 0; i < dashes.length; i++) { + dashes[i] = (float)(scale * dashes[i]); + } + dashphase = (float)(scale * dashphase); + } + width = (float)(scale * width); + pi = src.getPathIterator(at); + if (normalize != NormMode.OFF) { + pi = new NormalizingPathIterator(pi, normalize); + } + // leave inat and outat null. + } else { + // We only need the inverse if normalization is on. Otherwise + // we just don't transform the input paths, do all the stroking + // and then transform out output (instead of making PathIterator + // apply the transformation, us applying the inverse, and then + // us applying the transform again to our output). + outat = at; + if (normalize != NormMode.OFF) { + try { + inat = outat.createInverse(); + } catch (NoninvertibleTransformException e) { + // we made sure this can't happen + e.printStackTrace(); + } + pi = src.getPathIterator(at); + pi = new NormalizingPathIterator(pi, normalize); + } else { + pi = src.getPathIterator(null); + } + } + } else { + // either at is null or it's the identity. In either case + // we don't transform the path. + pi = src.getPathIterator(null); + if (normalize != NormMode.OFF) { + pi = new NormalizingPathIterator(pi, normalize); + } } - lsink = new Stroker(lsink, width, caps, join, miterlimit, a00, a01, a10, a11); + + pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, outat); + pc2d = new Stroker(pc2d, width, caps, join, miterlimit); if (dashes != null) { - lsink = new Dasher(lsink, dashes, dashphase, a00, a01, a10, a11); + pc2d = new Dasher(pc2d, dashes, dashphase); } - PathIterator pi; - if (normalize != NormMode.OFF) { - pi = new FlatteningPathIterator( - new NormalizingPathIterator(src.getPathIterator(at), normalize), - defaultFlat); - } else { - pi = src.getPathIterator(at, defaultFlat); - } - pathTo(pi, lsink); + pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, inat); + + pathTo(pi, pc2d); + } + + private static boolean nearZero(double num, int nulps) { + return Math.abs(num) < nulps * Math.ulp(num); } private static class NormalizingPathIterator implements PathIterator { @@ -337,10 +414,10 @@ } // normalize endpoint - float x_adjust = (float)Math.floor(coords[lastCoord] + lval) + rval - - coords[lastCoord]; - float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) + rval - - coords[lastCoord + 1]; + float x_adjust = (float)Math.floor(coords[lastCoord] + lval) + + rval - coords[lastCoord]; + float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) + + rval - coords[lastCoord + 1]; coords[lastCoord ] += x_adjust; coords[lastCoord + 1] += y_adjust; @@ -393,27 +470,9 @@ } } - void pathTo(PathIterator pi, LineSink lsink) { - float coords[] = new float[2]; - while (!pi.isDone()) { - switch (pi.currentSegment(coords)) { - case PathIterator.SEG_MOVETO: - lsink.moveTo(coords[0], coords[1]); - break; - case PathIterator.SEG_LINETO: - lsink.lineJoin(); - lsink.lineTo(coords[0], coords[1]); - break; - case PathIterator.SEG_CLOSE: - lsink.lineJoin(); - lsink.close(); - break; - default: - throw new InternalError("unknown flattened segment type"); - } - pi.next(); - } - lsink.end(); + static void pathTo(PathIterator pi, PathConsumer2D pc2d) { + RenderingEngine.feedConsumer(pi, pc2d); + pc2d.pathDone(); } /** @@ -471,32 +530,29 @@ boolean normalize, int bbox[]) { - PiscesCache pc = PiscesCache.createInstance(); Renderer r; NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF; if (bs == null) { PathIterator pi; if (normalize) { - pi = new FlatteningPathIterator( - new NormalizingPathIterator(s.getPathIterator(at), norm), - defaultFlat); + pi = new NormalizingPathIterator(s.getPathIterator(at), norm); } else { - pi = s.getPathIterator(at, defaultFlat); + pi = s.getPathIterator(at); } r = new Renderer(3, 3, clip.getLoX(), clip.getLoY(), clip.getWidth(), clip.getHeight(), - pi.getWindingRule(), pc); + pi.getWindingRule()); pathTo(pi, r); } else { r = new Renderer(3, 3, clip.getLoX(), clip.getLoY(), clip.getWidth(), clip.getHeight(), - PathIterator.WIND_NON_ZERO, pc); + PathIterator.WIND_NON_ZERO); strokeTo(s, at, bs, thin, norm, true, r); } r.endRendering(); - PiscesTileGenerator ptg = new PiscesTileGenerator(pc, r.MAX_AA_ALPHA); + PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA); ptg.getBbox(bbox); return ptg; }
--- a/jdk/src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/sun/java2d/pisces/PiscesTileGenerator.java Tue Nov 09 22:53:18 2010 -0800 @@ -25,40 +25,54 @@ package sun.java2d.pisces; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import sun.java2d.pipe.AATileGenerator; -public class PiscesTileGenerator implements AATileGenerator { - public static final int TILE_SIZE = 32; +public final class PiscesTileGenerator implements AATileGenerator { + public static final int TILE_SIZE = PiscesCache.TILE_SIZE; + + // perhaps we should be using weak references here, but right now + // that's not necessary. The way the renderer is, this map will + // never contain more than one element - the one with key 64, since + // we only do 8x8 supersampling. + private static final Map<Integer, byte[]> alphaMapsCache = new + ConcurrentHashMap<Integer, byte[]>(); PiscesCache cache; int x, y; - int maxalpha; + final int maxalpha; + private final int maxTileAlphaSum; + + // The alpha map used by this object (taken out of our map cache) to convert + // pixel coverage counts gotten from PiscesCache (which are in the range + // [0, maxalpha]) into alpha values, which are in [0,256). byte alphaMap[]; - public PiscesTileGenerator(PiscesCache cache, int maxalpha) { - this.cache = cache; + public PiscesTileGenerator(Renderer r, int maxalpha) { + this.cache = r.getCache(); this.x = cache.bboxX0; this.y = cache.bboxY0; this.alphaMap = getAlphaMap(maxalpha); this.maxalpha = maxalpha; + this.maxTileAlphaSum = TILE_SIZE*TILE_SIZE*maxalpha; } - static int prevMaxAlpha; - static byte prevAlphaMap[]; + private static byte[] buildAlphaMap(int maxalpha) { + byte[] alMap = new byte[maxalpha+1]; + int halfmaxalpha = maxalpha>>2; + for (int i = 0; i <= maxalpha; i++) { + alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha); + } + return alMap; + } - public synchronized static byte[] getAlphaMap(int maxalpha) { - if (maxalpha != prevMaxAlpha) { - prevAlphaMap = new byte[maxalpha+300]; - int halfmaxalpha = maxalpha>>2; - for (int i = 0; i <= maxalpha; i++) { - prevAlphaMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha); - } - for (int i = maxalpha; i < prevAlphaMap.length; i++) { - prevAlphaMap[i] = (byte) 255; - } - prevMaxAlpha = maxalpha; + public static byte[] getAlphaMap(int maxalpha) { + if (!alphaMapsCache.containsKey(maxalpha)) { + alphaMapsCache.put(maxalpha, buildAlphaMap(maxalpha)); } - return prevAlphaMap; + return alphaMapsCache.get(maxalpha); } public void getBbox(int bbox[]) { @@ -96,53 +110,24 @@ * value for partial coverage of the tile */ public int getTypicalAlpha() { - if (true) return 0x80; - // Decode run-length encoded alpha mask data - // The data for row j begins at cache.rowOffsetsRLE[j] - // and is encoded as a set of 2-byte pairs (val, runLen) - // terminated by a (0, 0) pair. - - int x0 = this.x; - int x1 = x0 + TILE_SIZE; - int y0 = this.y; - int y1 = y0 + TILE_SIZE; - if (x1 > cache.bboxX1) x1 = cache.bboxX1; - if (y1 > cache.bboxY1) y1 = cache.bboxY1; - y0 -= cache.bboxY0; - y1 -= cache.bboxY0; - - int ret = -1; - for (int cy = y0; cy < y1; cy++) { - int pos = cache.rowOffsetsRLE[cy]; - int cx = cache.minTouched[cy]; - - if (cx > x0) { - if (ret > 0) return 0x80; - ret = 0x00; - } - while (cx < x1) { - int runLen = cache.rowAARLE[pos + 1] & 0xff; - if (runLen == 0) { - if (ret > 0) return 0x80; - ret = 0x00; - break; - } - cx += runLen; - if (cx > x0) { - int val = cache.rowAARLE[pos] & 0xff; - if (ret != val) { - if (ret < 0) { - if (val != 0x00 && val != maxalpha) return 0x80; - ret = val; - } else { - return 0x80; - } - } - } - pos += 2; - } - } - return ret; + int al = cache.alphaSumInTile(x, y); + // Note: if we have a filled rectangle that doesn't end on a tile + // border, we could still return 0xff, even though al!=maxTileAlphaSum + // This is because if we return 0xff, our users will fill a rectangle + // starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x), + // and height min(TILE_SIZE,bboxY1-y), which is what should happen. + // However, to support this, we would have to use 2 Math.min's + // and 2 multiplications per tile, instead of just 2 multiplications + // to compute maxTileAlphaSum. The savings offered would probably + // not be worth it, considering how rare this case is. + // Note: I have not tested this, so in the future if it is determined + // that it is worth it, it should be implemented. Perhaps this method's + // interface should be changed to take arguments the width and height + // of the current tile. This would eliminate the 2 Math.min calls that + // would be needed here, since our caller needs to compute these 2 + // values anyway. + return (al == 0x00 ? 0x00 : + (al == maxTileAlphaSum ? 0xff : 0x80)); } /** @@ -179,22 +164,24 @@ int idx = offset; for (int cy = y0; cy < y1; cy++) { - int pos = cache.rowOffsetsRLE[cy]; - int cx = cache.minTouched[cy]; + int[] row = cache.rowAARLE[cy]; + assert row != null; + int cx = cache.minTouched(cy); if (cx > x1) cx = x1; - if (cx > x0) { - //System.out.println("L["+(cx-x0)+"]"); - for (int i = x0; i < cx; i++) { - tile[idx++] = 0x00; - } + for (int i = x0; i < cx; i++) { + tile[idx++] = 0x00; } - while (cx < x1) { + + int pos = 2; + while (cx < x1 && pos < row[1]) { byte val; int runLen = 0; + assert row[1] > 2; try { - val = alphaMap[cache.rowAARLE[pos] & 0xff]; - runLen = cache.rowAARLE[pos + 1] & 0xff; + val = alphaMap[row[pos]]; + runLen = row[pos + 1]; + assert runLen > 0; } catch (RuntimeException e0) { System.out.println("maxalpha = "+maxalpha); System.out.println("tile["+x0+", "+y0+ @@ -202,14 +189,12 @@ System.out.println("cx = "+cx+", cy = "+cy); System.out.println("idx = "+idx+", pos = "+pos); System.out.println("len = "+runLen); - cache.print(System.out); + System.out.print(cache.toString()); e0.printStackTrace(); System.exit(1); return; } - if (runLen == 0) { - break; - } + int rx0 = cx; cx += runLen; int rx1 = cx; @@ -228,7 +213,7 @@ System.out.println("idx = "+idx+", pos = "+pos); System.out.println("rx0 = "+rx0+", rx1 = "+rx1); System.out.println("len = "+runLen); - cache.print(System.out); + System.out.print(cache.toString()); e.printStackTrace(); System.exit(1); return; @@ -265,4 +250,4 @@ * No further calls will be made on this instance. */ public void dispose() {} -} +} \ No newline at end of file
--- a/jdk/src/share/classes/sun/java2d/pisces/Renderer.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/sun/java2d/pisces/Renderer.java Tue Nov 09 22:53:18 2010 -0800 @@ -26,250 +26,552 @@ package sun.java2d.pisces; import java.util.Arrays; +import java.util.Iterator; -public class Renderer implements LineSink { +import sun.awt.geom.PathConsumer2D; -/////////////////////////////////////////////////////////////////////////////// -// Scan line iterator and edge crossing data. -////////////////////////////////////////////////////////////////////////////// +public class Renderer implements PathConsumer2D { - private int[] crossings; + private class ScanlineIterator { - // This is an array of indices into the edge array. It is initialized to - // [i * SIZEOF_STRUCT_EDGE for i in range(0, edgesSize/SIZEOF_STRUCT_EDGE)] - // (where range(i, j) is i,i+1,...,j-1 -- just like in python). - // The reason for keeping this is because we need the edges array sorted - // by y0, but we don't want to move all that data around, so instead we - // sort the indices into the edge array, and use edgeIndices to access - // the edges array. This is meant to simulate a pointer array (hence the name) - private int[] edgePtrs; + private int[] crossings; - // crossing bounds. The bounds are not necessarily tight (the scan line - // at minY, for example, might have no crossings). The x bounds will - // be accumulated as crossings are computed. - private int minY, maxY; - private int minX, maxX; - private int nextY; + // crossing bounds. The bounds are not necessarily tight (the scan line + // at minY, for example, might have no crossings). The x bounds will + // be accumulated as crossings are computed. + private int minY, maxY; + private int nextY; - // indices into the edge pointer list. They indicate the "active" sublist in - // the edge list (the portion of the list that contains all the edges that - // cross the next scan line). - private int lo, hi; + // indices into the segment pointer lists. They indicate the "active" + // sublist in the segment lists (the portion of the list that contains + // all the segments that cross the next scan line). + private int elo, ehi; + private final int[] edgePtrs; + private int qlo, qhi; + private final int[] quadPtrs; + private int clo, chi; + private final int[] curvePtrs; - private static final int INIT_CROSSINGS_SIZE = 50; - private void ScanLineItInitialize() { - crossings = new int[INIT_CROSSINGS_SIZE]; - edgePtrs = new int[edgesSize / SIZEOF_STRUCT_EDGE]; - for (int i = 0; i < edgePtrs.length; i++) { - edgePtrs[i] = i * SIZEOF_STRUCT_EDGE; + private static final int INIT_CROSSINGS_SIZE = 10; + + private ScanlineIterator() { + crossings = new int[INIT_CROSSINGS_SIZE]; + + edgePtrs = new int[numEdges]; + Helpers.fillWithIdxes(edgePtrs, SIZEOF_EDGE); + qsort(edges, edgePtrs, YMIN, 0, numEdges - 1); + + quadPtrs = new int[numQuads]; + Helpers.fillWithIdxes(quadPtrs, SIZEOF_QUAD); + qsort(quads, quadPtrs, YMIN, 0, numQuads - 1); + + curvePtrs = new int[numCurves]; + Helpers.fillWithIdxes(curvePtrs, SIZEOF_CURVE); + qsort(curves, curvePtrs, YMIN, 0, numCurves - 1); + + // We don't care if we clip some of the line off with ceil, since + // no scan line crossings will be eliminated (in fact, the ceil is + // the y of the first scan line crossing). + nextY = minY = Math.max(boundsMinY, (int)Math.ceil(edgeMinY)); + maxY = Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY)); + + for (elo = 0; elo < numEdges && edges[edgePtrs[elo]+YMAX] <= minY; elo++) + ; + // the active list is *edgePtrs[lo] (inclusive) *edgePtrs[hi] (exclusive) + for (ehi = elo; ehi < numEdges && edges[edgePtrs[ehi]+YMIN] <= minY; ehi++) + edgeSetCurY(edgePtrs[ehi], minY);// TODO: make minY a float to avoid casts + + for (qlo = 0; qlo < numQuads && quads[quadPtrs[qlo]+YMAX] <= minY; qlo++) + ; + for (qhi = qlo; qhi < numQuads && quads[quadPtrs[qhi]+YMIN] <= minY; qhi++) + quadSetCurY(quadPtrs[qhi], minY); + + for (clo = 0; clo < numCurves && curves[curvePtrs[clo]+YMAX] <= minY; clo++) + ; + for (chi = clo; chi < numCurves && curves[curvePtrs[chi]+YMIN] <= minY; chi++) + curveSetCurY(curvePtrs[chi], minY); } - qsort(0, edgePtrs.length - 1); + private int next() { + // we go through the active lists and remove segments that don't cross + // the nextY scanline. + int crossingIdx = 0; + for (int i = elo; i < ehi; i++) { + if (edges[edgePtrs[i]+YMAX] <= nextY) { + edgePtrs[i] = edgePtrs[elo++]; + } + } + for (int i = qlo; i < qhi; i++) { + if (quads[quadPtrs[i]+YMAX] <= nextY) { + quadPtrs[i] = quadPtrs[qlo++]; + } + } + for (int i = clo; i < chi; i++) { + if (curves[curvePtrs[i]+YMAX] <= nextY) { + curvePtrs[i] = curvePtrs[clo++]; + } + } - // We don't care if we clip some of the line off with ceil, since - // no scan line crossings will be eliminated (in fact, the ceil is - // the y of the first scan line crossing). - nextY = minY = Math.max(boundsMinY, (int)Math.ceil(edgeMinY)); - maxY = Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY)); + crossings = Helpers.widenArray(crossings, 0, ehi-elo+qhi-qlo+chi-clo); - for (lo = 0; lo < edgePtrs.length && edges[edgePtrs[lo]+Y1] <= nextY; lo++) - ; - for (hi = lo; hi < edgePtrs.length && edges[edgePtrs[hi]+CURY] <= nextY; hi++) - ; // the active list is *edgePtrs[lo] (inclusive) *edgePtrs[hi] (exclusive) - for (int i = lo; i < hi; i++) { - setCurY(edgePtrs[i], nextY); + // Now every edge between lo and hi crosses nextY. Compute it's + // crossing and put it in the crossings array. + for (int i = elo; i < ehi; i++) { + int ptr = edgePtrs[i]; + addCrossing(nextY, (int)edges[ptr+CURX], edges[ptr+OR], crossingIdx); + edgeGoToNextY(ptr); + crossingIdx++; + } + for (int i = qlo; i < qhi; i++) { + int ptr = quadPtrs[i]; + addCrossing(nextY, (int)quads[ptr+CURX], quads[ptr+OR], crossingIdx); + quadGoToNextY(ptr); + crossingIdx++; + } + for (int i = clo; i < chi; i++) { + int ptr = curvePtrs[i]; + addCrossing(nextY, (int)curves[ptr+CURX], curves[ptr+OR], crossingIdx); + curveGoToNextY(ptr); + crossingIdx++; + } + + nextY++; + // Expand active lists to include new edges. + for (; ehi < numEdges && edges[edgePtrs[ehi]+YMIN] <= nextY; ehi++) { + edgeSetCurY(edgePtrs[ehi], nextY); + } + for (; qhi < numQuads && quads[quadPtrs[qhi]+YMIN] <= nextY; qhi++) { + quadSetCurY(quadPtrs[qhi], nextY); + } + for (; chi < numCurves && curves[curvePtrs[chi]+YMIN] <= nextY; chi++) { + curveSetCurY(curvePtrs[chi], nextY); + } + Arrays.sort(crossings, 0, crossingIdx); + return crossingIdx; } - // We accumulate X in the iterator because accumulating it in addEdge - // like we do with Y does not do much good: if there's an edge - // (0,0)->(1000,10000), and if y gets clipped to 1000, then the x - // bound should be 100, but the accumulator from addEdge would say 1000, - // so we'd still have to accumulate the X bounds as we add crossings. - minX = boundsMinX; - maxX = boundsMaxX; - } - - private int ScanLineItCurrentY() { - return nextY - 1; - } - - private int ScanLineItGoToNextYAndComputeCrossings() { - // we go through the active list and remove the ones that don't cross - // the nextY scanline. - int crossingIdx = 0; - for (int i = lo; i < hi; i++) { - if (edges[edgePtrs[i]+Y1] <= nextY) { - edgePtrs[i] = edgePtrs[lo++]; - } - } - if (hi - lo > crossings.length) { - int newSize = Math.max(hi - lo, crossings.length * 2); - crossings = Arrays.copyOf(crossings, newSize); - } - // Now every edge between lo and hi crosses nextY. Compute it's - // crossing and put it in the crossings array. - for (int i = lo; i < hi; i++) { - addCrossing(nextY, getCurCrossing(edgePtrs[i]), (int)edges[edgePtrs[i]+OR], crossingIdx); - gotoNextY(edgePtrs[i]); - crossingIdx++; + private boolean hasNext() { + return nextY < maxY; } - nextY++; - // Expand active list to include new edges. - for (; hi < edgePtrs.length && edges[edgePtrs[hi]+CURY] <= nextY; hi++) { - setCurY(edgePtrs[hi], nextY); + private int curY() { + return nextY - 1; } - Arrays.sort(crossings, 0, crossingIdx); - return crossingIdx; + private void addCrossing(int y, int x, float or, int idx) { + x <<= 1; + crossings[idx] = ((or > 0) ? (x | 0x1) : x); + } } - - private boolean ScanLineItHasNext() { - return nextY < maxY; - } - - private void addCrossing(int y, int x, int or, int idx) { - if (x < minX) { - minX = x; - } - if (x > maxX) { - maxX = x; - } - x <<= 1; - crossings[idx] = ((or == 1) ? (x | 0x1) : x); - } - - // quicksort implementation for sorting the edge indices ("pointers") // by increasing y0. first, last are indices into the "pointer" array // It sorts the pointer array from first (inclusive) to last (inclusive) - private void qsort(int first, int last) { + private static void qsort(final float[] data, final int[] ptrs, + final int fieldForCmp, int first, int last) + { if (last > first) { - int p = partition(first, last); + int p = partition(data, ptrs, fieldForCmp, first, last); if (first < p - 1) { - qsort(first, p - 1); + qsort(data, ptrs, fieldForCmp, first, p - 1); } if (p < last) { - qsort(p, last); + qsort(data, ptrs, fieldForCmp, p, last); } } } // i, j are indices into edgePtrs. - private int partition(int i, int j) { - int pivotVal = edgePtrs[i]; + private static int partition(final float[] data, final int[] ptrs, + final int fieldForCmp, int i, int j) + { + int pivotValFieldForCmp = ptrs[i]+fieldForCmp; while (i <= j) { // edges[edgePtrs[i]+1] is equivalent to (*(edgePtrs[i])).y0 in C - while (edges[edgePtrs[i]+CURY] < edges[pivotVal+CURY]) { i++; } - while (edges[edgePtrs[j]+CURY] > edges[pivotVal+CURY]) { j--; } + while (data[ptrs[i]+fieldForCmp] < data[pivotValFieldForCmp]) + i++; + while (data[ptrs[j]+fieldForCmp] > data[pivotValFieldForCmp]) + j--; if (i <= j) { - int tmp = edgePtrs[i]; - edgePtrs[i] = edgePtrs[j]; - edgePtrs[j] = tmp; + int tmp = ptrs[i]; + ptrs[i] = ptrs[j]; + ptrs[j] = tmp; i++; j--; } } return i; } - //============================================================================ ////////////////////////////////////////////////////////////////////////////// // EDGE LIST ////////////////////////////////////////////////////////////////////////////// +// TODO(maybe): very tempting to use fixed point here. A lot of opportunities +// for shifts and just removing certain operations altogether. +// TODO: it might be worth it to make an EdgeList class. It would probably +// clean things up a bit and not impact performance much. - private static final int INIT_NUM_EDGES = 1000; - private static final int SIZEOF_STRUCT_EDGE = 5; + // common to all types of input path segments. + private static final int YMIN = 0; + private static final int YMAX = 1; + private static final int CURX = 2; + // this and OR are meant to be indeces into "int" fields, but arrays must + // be homogenous, so every field is a float. However floats can represent + // exactly up to 26 bit ints, so we're ok. + private static final int CURY = 3; + private static final int OR = 4; - // The following array is a poor man's struct array: - // it simulates a struct array by having - // edges[SIZEOF_STRUCT_EDGE * i + j] be the jth field in the ith element - // of an array of edge structs. - private float[] edges; - private int edgesSize; // size of the edge list. - private static final int Y1 = 0; - private static final int SLOPE = 1; - private static final int OR = 2; // the orientation. This can be -1 or 1. - // -1 means up, 1 means down. - private static final int CURY = 3; // j = 5 corresponds to the "current Y". - // Each edge keeps track of the last scanline - // crossing it computed, and this is the y coord of - // that scanline. - private static final int CURX = 4; //the x coord of the current crossing. + // for straight lines only: + private static final int SLOPE = 5; - // Note that while the array is declared as a float[] not all of it's - // elements should be floats. currentY and Orientation should be ints (or int and - // byte respectively), but they all need to be the same type. This isn't - // really a problem because floats can represent exactly all 23 bit integers, - // which should be more than enough. - // Note, also, that we only need x1 for slope computation, so we don't need - // to store it. x0, y0 don't need to be stored either. They can be put into - // curx, cury, and it's ok if they're lost when curx and cury are changed. - // We take this undeniably ugly and error prone approach (instead of simply - // making an Edge class) for performance reasons. Also, it would probably be nicer - // to have one array for each field, but that would defeat the purpose because - // it would make poor use of the processor cache, since we tend to access - // all the fields for one edge at a time. + // for quads and cubics: + private static final int X0 = 5; + private static final int Y0 = 6; + private static final int XL = 7; + private static final int COUNT = 8; + private static final int CURSLOPE = 9; + private static final int DX = 10; + private static final int DY = 11; + private static final int DDX = 12; + private static final int DDY = 13; - private float edgeMinY; - private float edgeMaxY; + // for cubics only + private static final int DDDX = 14; + private static final int DDDY = 15; + private float edgeMinY = Float.POSITIVE_INFINITY; + private float edgeMaxY = Float.NEGATIVE_INFINITY; + private float edgeMinX = Float.POSITIVE_INFINITY; + private float edgeMaxX = Float.NEGATIVE_INFINITY; - private void addEdge(float x0, float y0, float x1, float y1) { - float or = (y0 < y1) ? 1f : -1f; // orientation: 1 = UP; -1 = DOWN - if (or == -1) { - float tmp = y0; - y0 = y1; - y1 = tmp; - tmp = x0; - x0 = x1; - x1 = tmp; + private static final int SIZEOF_EDGE = 6; + private float[] edges = null; + private int numEdges; + // these are static because we need them to be usable from ScanlineIterator + private void edgeSetCurY(final int idx, int y) { + edges[idx+CURX] += (y - edges[idx+CURY]) * edges[idx+SLOPE]; + edges[idx+CURY] = y; + } + private void edgeGoToNextY(final int idx) { + edges[idx+CURY] += 1; + edges[idx+CURX] += edges[idx+SLOPE]; + } + + + private static final int SIZEOF_QUAD = 14; + private float[] quads = null; + private int numQuads; + // This function should be called exactly once, to set the first scanline + // of the curve. Before it is called, the curve should think its first + // scanline is CEIL(YMIN). + private void quadSetCurY(final int idx, final int y) { + assert y < quads[idx+YMAX]; + assert (quads[idx+CURY] > y); + assert (quads[idx+CURY] == Math.ceil(quads[idx+CURY])); + + while (quads[idx+CURY] < ((float)y)) { + quadGoToNextY(idx); } - // skip edges that don't cross a scanline - if (Math.ceil(y0) >= Math.ceil(y1)) { + } + private void quadGoToNextY(final int idx) { + quads[idx+CURY] += 1; + // this will get overriden if the while executes. + quads[idx+CURX] += quads[idx+CURSLOPE]; + int count = (int)quads[idx+COUNT]; + // this loop should never execute more than once because our + // curve is monotonic in Y. Still we put it in because you can + // never be too sure when dealing with floating point. + while(quads[idx+CURY] >= quads[idx+Y0] && count > 0) { + float x0 = quads[idx+X0], y0 = quads[idx+Y0]; + count = executeQuadAFDIteration(idx); + float x1 = quads[idx+X0], y1 = quads[idx+Y0]; + // our quads are monotonic, so this shouldn't happen, but + // it is conceivable that for very flat quads with different + // y values at their endpoints AFD might give us a horizontal + // segment. + if (y1 == y0) { + continue; + } + quads[idx+CURSLOPE] = (x1 - x0) / (y1 - y0); + quads[idx+CURX] = x0 + (quads[idx+CURY] - y0) * quads[idx+CURSLOPE]; + } + } + + + private static final int SIZEOF_CURVE = 16; + private float[] curves = null; + private int numCurves; + private void curveSetCurY(final int idx, final int y) { + assert y < curves[idx+YMAX]; + assert (curves[idx+CURY] > y); + assert (curves[idx+CURY] == Math.ceil(curves[idx+CURY])); + + while (curves[idx+CURY] < ((float)y)) { + curveGoToNextY(idx); + } + } + private void curveGoToNextY(final int idx) { + curves[idx+CURY] += 1; + // this will get overriden if the while executes. + curves[idx+CURX] += curves[idx+CURSLOPE]; + int count = (int)curves[idx+COUNT]; + // this loop should never execute more than once because our + // curve is monotonic in Y. Still we put it in because you can + // never be too sure when dealing with floating point. + while(curves[idx+CURY] >= curves[idx+Y0] && count > 0) { + float x0 = curves[idx+X0], y0 = curves[idx+Y0]; + count = executeCurveAFDIteration(idx); + float x1 = curves[idx+X0], y1 = curves[idx+Y0]; + // our curves are monotonic, so this shouldn't happen, but + // it is conceivable that for very flat curves with different + // y values at their endpoints AFD might give us a horizontal + // segment. + if (y1 == y0) { + continue; + } + curves[idx+CURSLOPE] = (x1 - x0) / (y1 - y0); + curves[idx+CURX] = x0 + (curves[idx+CURY] - y0) * curves[idx+CURSLOPE]; + } + } + + + private static final float DEC_BND = 20f; + private static final float INC_BND = 8f; + // Flattens using adaptive forward differencing. This only carries out + // one iteration of the AFD loop. All it does is update AFD variables (i.e. + // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings). + private int executeQuadAFDIteration(int idx) { + int count = (int)quads[idx+COUNT]; + float ddx = quads[idx+DDX]; + float ddy = quads[idx+DDY]; + float dx = quads[idx+DX]; + float dy = quads[idx+DY]; + + while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) { + ddx = ddx / 4; + ddy = ddy / 4; + dx = (dx - ddx) / 2; + dy = (dy - ddy) / 2; + count <<= 1; + } + // can only do this on even "count" values, because we must divide count by 2 + while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) { + dx = 2 * dx + ddx; + dy = 2 * dy + ddy; + ddx = 4 * ddx; + ddy = 4 * ddy; + count >>= 1; + } + count--; + if (count > 0) { + quads[idx+X0] += dx; + dx += ddx; + quads[idx+Y0] += dy; + dy += ddy; + } else { + quads[idx+X0] = quads[idx+XL]; + quads[idx+Y0] = quads[idx+YMAX]; + } + quads[idx+COUNT] = count; + quads[idx+DDX] = ddx; + quads[idx+DDY] = ddy; + quads[idx+DX] = dx; + quads[idx+DY] = dy; + return count; + } + private int executeCurveAFDIteration(int idx) { + int count = (int)curves[idx+COUNT]; + float ddx = curves[idx+DDX]; + float ddy = curves[idx+DDY]; + float dx = curves[idx+DX]; + float dy = curves[idx+DY]; + float dddx = curves[idx+DDDX]; + float dddy = curves[idx+DDDY]; + + while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) { + dddx /= 8; + dddy /= 8; + ddx = ddx/4 - dddx; + ddy = ddy/4 - dddy; + dx = (dx - ddx) / 2; + dy = (dy - ddy) / 2; + count <<= 1; + } + // can only do this on even "count" values, because we must divide count by 2 + while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) { + dx = 2 * dx + ddx; + dy = 2 * dy + ddy; + ddx = 4 * (ddx + dddx); + ddy = 4 * (ddy + dddy); + dddx = 8 * dddx; + dddy = 8 * dddy; + count >>= 1; + } + count--; + if (count > 0) { + curves[idx+X0] += dx; + dx += ddx; + ddx += dddx; + curves[idx+Y0] += dy; + dy += ddy; + ddy += dddy; + } else { + curves[idx+X0] = curves[idx+XL]; + curves[idx+Y0] = curves[idx+YMAX]; + } + curves[idx+COUNT] = count; + curves[idx+DDDX] = dddx; + curves[idx+DDDY] = dddy; + curves[idx+DDX] = ddx; + curves[idx+DDY] = ddy; + curves[idx+DX] = dx; + curves[idx+DY] = dy; + return count; + } + + + private void initLine(final int idx, float[] pts, int or) { + edges[idx+SLOPE] = (pts[2] - pts[0]) / (pts[3] - pts[1]); + edges[idx+CURX] = pts[0] + (edges[idx+CURY] - pts[1]) * edges[idx+SLOPE]; + } + + private void initQuad(final int idx, float[] points, int or) { + final int countlg = 3; + final int count = 1 << countlg; + + // the dx and dy refer to forward differencing variables, not the last + // coefficients of the "points" polynomial + final float ddx, ddy, dx, dy; + c.set(points, 6); + + ddx = c.dbx / (1 << (2 * countlg)); + ddy = c.dby / (1 << (2 * countlg)); + dx = c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg); + dy = c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg); + + quads[idx+DDX] = ddx; + quads[idx+DDY] = ddy; + quads[idx+DX] = dx; + quads[idx+DY] = dy; + quads[idx+COUNT] = count; + quads[idx+XL] = points[4]; + quads[idx+X0] = points[0]; + quads[idx+Y0] = points[1]; + executeQuadAFDIteration(idx); + float x1 = quads[idx+X0], y1 = quads[idx+Y0]; + quads[idx+CURSLOPE] = (x1 - points[0]) / (y1 - points[1]); + quads[idx+CURX] = points[0] + (quads[idx+CURY] - points[1])*quads[idx+CURSLOPE]; + } + + private void initCurve(final int idx, float[] points, int or) { + final int countlg = 3; + final int count = 1 << countlg; + + // the dx and dy refer to forward differencing variables, not the last + // coefficients of the "points" polynomial + final float dddx, dddy, ddx, ddy, dx, dy; + c.set(points, 8); + dddx = 2f * c.dax / (1 << (3 * countlg)); + dddy = 2f * c.day / (1 << (3 * countlg)); + + ddx = dddx + c.dbx / (1 << (2 * countlg)); + ddy = dddy + c.dby / (1 << (2 * countlg)); + dx = c.ax / (1 << (3 * countlg)) + c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg); + dy = c.ay / (1 << (3 * countlg)) + c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg); + + curves[idx+DDDX] = dddx; + curves[idx+DDDY] = dddy; + curves[idx+DDX] = ddx; + curves[idx+DDY] = ddy; + curves[idx+DX] = dx; + curves[idx+DY] = dy; + curves[idx+COUNT] = count; + curves[idx+XL] = points[6]; + curves[idx+X0] = points[0]; + curves[idx+Y0] = points[1]; + executeCurveAFDIteration(idx); + float x1 = curves[idx+X0], y1 = curves[idx+Y0]; + curves[idx+CURSLOPE] = (x1 - points[0]) / (y1 - points[1]); + curves[idx+CURX] = points[0] + (curves[idx+CURY] - points[1])*curves[idx+CURSLOPE]; + } + + private void addPathSegment(float[] pts, final int type, final int or) { + int idx; + float[] addTo; + switch (type) { + case 4: + idx = numEdges * SIZEOF_EDGE; + addTo = edges = Helpers.widenArray(edges, numEdges*SIZEOF_EDGE, SIZEOF_EDGE); + numEdges++; + break; + case 6: + idx = numQuads * SIZEOF_QUAD; + addTo = quads = Helpers.widenArray(quads, numQuads*SIZEOF_QUAD, SIZEOF_QUAD); + numQuads++; + break; + case 8: + idx = numCurves * SIZEOF_CURVE; + addTo = curves = Helpers.widenArray(curves, numCurves*SIZEOF_CURVE, SIZEOF_CURVE); + numCurves++; + break; + default: + throw new InternalError(); + } + // set the common fields, except CURX, for which we must know the kind + // of curve. NOTE: this must be done before the type specific fields + // are initialized, because those depend on the common ones. + addTo[idx+YMIN] = pts[1]; + addTo[idx+YMAX] = pts[type-1]; + addTo[idx+OR] = or; + addTo[idx+CURY] = (float)Math.ceil(pts[1]); + switch (type) { + case 4: + initLine(idx, pts, or); + break; + case 6: + initQuad(idx, pts, or); + break; + case 8: + initCurve(idx, pts, or); + break; + default: + throw new InternalError(); + } + } + + // precondition: the curve in pts must be monotonic and increasing in y. + private void somethingTo(float[] pts, final int type, final int or) { + // NOTE: it's very important that we check for or >= 0 below (as + // opposed to or == 1, or or > 0, or anything else). That's + // because if we check for or==1, when the curve being added + // is a horizontal line, or will be 0 so or==1 will be false and + // x0 and y0 will be updated to pts[0] and pts[1] instead of pts[type-2] + // and pts[type-1], which is the correct thing to do. + this.x0 = or >= 0 ? pts[type - 2] : pts[0]; + this.y0 = or >= 0 ? pts[type - 1] : pts[1]; + + float minY = pts[1], maxY = pts[type - 1]; + if (Math.ceil(minY) >= Math.ceil(maxY) || + Math.ceil(minY) >= boundsMaxY || maxY < boundsMinY) + { return; } - int newSize = edgesSize + SIZEOF_STRUCT_EDGE; - if (edges.length < newSize) { - edges = Arrays.copyOf(edges, newSize * 2); - } - edges[edgesSize+CURX] = x0; - edges[edgesSize+CURY] = y0; - edges[edgesSize+Y1] = y1; - edges[edgesSize+SLOPE] = (x1 - x0) / (y1 - y0); - edges[edgesSize+OR] = or; - // the crossing values can't be initialized meaningfully yet. This - // will have to wait until setCurY is called - edgesSize += SIZEOF_STRUCT_EDGE; + if (minY < edgeMinY) { edgeMinY = minY; } + if (maxY > edgeMaxY) { edgeMaxY = maxY; } - // Accumulate edgeMinY and edgeMaxY - if (y0 < edgeMinY) { edgeMinY = y0; } - if (y1 > edgeMaxY) { edgeMaxY = y1; } + int minXidx = (pts[0] < pts[type-2] ? 0 : type - 2); + float minX = pts[minXidx]; + float maxX = pts[type - 2 - minXidx]; + if (minX < edgeMinX) { edgeMinX = minX; } + if (maxX > edgeMaxX) { edgeMaxX = maxX; } + addPathSegment(pts, type, or); } - // As far as the following methods care, this edges extends to infinity. - // They can compute the x intersect of any horizontal line. - // precondition: idx is the index to the start of the desired edge. - // So, if the ith edge is wanted, idx should be SIZEOF_STRUCT_EDGE * i - private void setCurY(int idx, int y) { - // compute the x crossing of edge at idx and horizontal line y - // currentXCrossing = (y - y0)*slope + x0 - edges[idx + CURX] = (y - edges[idx + CURY]) * edges[idx + SLOPE] + edges[idx+CURX]; - edges[idx + CURY] = (float)y; - } +// END EDGE LIST +////////////////////////////////////////////////////////////////////////////// - private void gotoNextY(int idx) { - edges[idx + CURY] += 1f; // i.e. curY += 1 - edges[idx + CURX] += edges[idx + SLOPE]; // i.e. curXCrossing += slope - } - - private int getCurCrossing(int idx) { - return (int)edges[idx + CURX]; - } -//==================================================================================== public static final int WIND_EVEN_ODD = 0; public static final int WIND_NON_ZERO = 1; @@ -284,16 +586,13 @@ final int MAX_AA_ALPHA; // Cache to store RLE-encoded coverage mask of the current primitive - final PiscesCache cache; + PiscesCache cache; // Bounds of the drawing region, at subpixel precision. - final private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY; - - // Pixel bounding box for current primitive - private int pix_bboxX0, pix_bboxY0, pix_bboxX1, pix_bboxY1; + private final int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY; // Current winding rule - final private int windingRule; + private final int windingRule; // Current drawing position, i.e., final point of last segment private float x0, y0; @@ -304,8 +603,8 @@ public Renderer(int subpixelLgPositionsX, int subpixelLgPositionsY, int pix_boundsX, int pix_boundsY, int pix_boundsWidth, int pix_boundsHeight, - int windingRule, - PiscesCache cache) { + int windingRule) + { this.SUBPIXEL_LG_POSITIONS_X = subpixelLgPositionsX; this.SUBPIXEL_LG_POSITIONS_Y = subpixelLgPositionsY; this.SUBPIXEL_MASK_X = (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1; @@ -314,23 +613,12 @@ this.SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y); this.MAX_AA_ALPHA = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y); - this.edges = new float[SIZEOF_STRUCT_EDGE * INIT_NUM_EDGES]; - edgeMinY = Float.POSITIVE_INFINITY; - edgeMaxY = Float.NEGATIVE_INFINITY; - edgesSize = 0; - this.windingRule = windingRule; - this.cache = cache; this.boundsMinX = pix_boundsX * SUBPIXEL_POSITIONS_X; this.boundsMinY = pix_boundsY * SUBPIXEL_POSITIONS_Y; this.boundsMaxX = (pix_boundsX + pix_boundsWidth) * SUBPIXEL_POSITIONS_X; this.boundsMaxY = (pix_boundsY + pix_boundsHeight) * SUBPIXEL_POSITIONS_Y; - - this.pix_bboxX0 = pix_boundsX; - this.pix_bboxY0 = pix_boundsY; - this.pix_bboxX1 = pix_boundsX + pix_boundsWidth; - this.pix_bboxY1 = pix_boundsY + pix_boundsHeight; } private float tosubpixx(float pix_x) { @@ -341,7 +629,7 @@ } public void moveTo(float pix_x0, float pix_y0) { - close(); + closePath(); this.pix_sx0 = pix_x0; this.pix_sy0 = pix_y0; this.y0 = tosubpixy(pix_y0); @@ -350,39 +638,102 @@ public void lineJoin() { /* do nothing */ } - public void lineTo(float pix_x1, float pix_y1) { - float x1 = tosubpixx(pix_x1); - float y1 = tosubpixy(pix_y1); + private final float[][] pts = new float[2][8]; + private final float[] ts = new float[4]; - // Ignore horizontal lines - if (y0 == y1) { - this.x0 = x1; - return; + private static void invertPolyPoints(float[] pts, int off, int type) { + for (int i = off, j = off + type - 2; i < j; i += 2, j -= 2) { + float tmp = pts[i]; + pts[i] = pts[j]; + pts[j] = tmp; + tmp = pts[i+1]; + pts[i+1] = pts[j+1]; + pts[j+1] = tmp; } - - addEdge(x0, y0, x1, y1); - - this.x0 = x1; - this.y0 = y1; } - public void close() { + // return orientation before making the curve upright. + private static int makeMonotonicCurveUpright(float[] pts, int off, int type) { + float y0 = pts[off + 1]; + float y1 = pts[off + type - 1]; + if (y0 > y1) { + invertPolyPoints(pts, off, type); + return -1; + } else if (y0 < y1) { + return 1; + } + return 0; + } + + public void lineTo(float pix_x1, float pix_y1) { + pts[0][0] = x0; pts[0][1] = y0; + pts[0][2] = tosubpixx(pix_x1); pts[0][3] = tosubpixy(pix_y1); + int or = makeMonotonicCurveUpright(pts[0], 0, 4); + somethingTo(pts[0], 4, or); + } + + Curve c = new Curve(); + private void curveOrQuadTo(int type) { + c.set(pts[0], type); + int numTs = c.dxRoots(ts, 0); + numTs += c.dyRoots(ts, numTs); + numTs = Helpers.filterOutNotInAB(ts, 0, numTs, 0, 1); + Helpers.isort(ts, 0, numTs); + + Iterator<float[]> it = Curve.breakPtsAtTs(pts, type, ts, numTs); + while(it.hasNext()) { + float[] curCurve = it.next(); + int or = makeMonotonicCurveUpright(curCurve, 0, type); + somethingTo(curCurve, type, or); + } + } + + @Override public void curveTo(float x1, float y1, + float x2, float y2, + float x3, float y3) + { + pts[0][0] = x0; pts[0][1] = y0; + pts[0][2] = tosubpixx(x1); pts[0][3] = tosubpixy(y1); + pts[0][4] = tosubpixx(x2); pts[0][5] = tosubpixy(y2); + pts[0][6] = tosubpixx(x3); pts[0][7] = tosubpixy(y3); + curveOrQuadTo(8); + } + + @Override public void quadTo(float x1, float y1, float x2, float y2) { + pts[0][0] = x0; pts[0][1] = y0; + pts[0][2] = tosubpixx(x1); pts[0][3] = tosubpixy(y1); + pts[0][4] = tosubpixx(x2); pts[0][5] = tosubpixy(y2); + curveOrQuadTo(6); + } + + public void closePath() { // lineTo expects its input in pixel coordinates. lineTo(pix_sx0, pix_sy0); } - public void end() { - close(); + public void pathDone() { + closePath(); } - private void _endRendering() { + + @Override + public long getNativeConsumer() { + throw new InternalError("Renderer does not use a native consumer."); + } + + private void _endRendering(final int pix_bboxx0, final int pix_bboxy0, + final int pix_bboxx1, final int pix_bboxy1) + { // Mask to determine the relevant bit of the crossing sum // 0x1 if EVEN_ODD, all bits if NON_ZERO int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0; // add 1 to better deal with the last pixel in a pixel row. - int width = ((boundsMaxX - boundsMinX) >> SUBPIXEL_LG_POSITIONS_X) + 1; - byte[] alpha = new byte[width+1]; + int width = pix_bboxx1 - pix_bboxx0 + 1; + int[] alpha = new int[width+1]; + + int bboxx0 = pix_bboxx0 << SUBPIXEL_LG_POSITIONS_X; + int bboxx1 = pix_bboxx1 << SUBPIXEL_LG_POSITIONS_X; // Now we iterate through the scanlines. We must tell emitRow the coord // of the first non-transparent pixel, so we must keep accumulators for @@ -394,33 +745,34 @@ int pix_minX = Integer.MAX_VALUE; int y = boundsMinY; // needs to be declared here so we emit the last row properly. - ScanLineItInitialize(); - for ( ; ScanLineItHasNext(); ) { - int numCrossings = ScanLineItGoToNextYAndComputeCrossings(); - y = ScanLineItCurrentY(); + ScanlineIterator it = this.new ScanlineIterator(); + for ( ; it.hasNext(); ) { + int numCrossings = it.next(); + int[] crossings = it.crossings; + y = it.curY(); if (numCrossings > 0) { int lowx = crossings[0] >> 1; int highx = crossings[numCrossings - 1] >> 1; - int x0 = Math.max(lowx, boundsMinX); - int x1 = Math.min(highx, boundsMaxX); + int x0 = Math.max(lowx, bboxx0); + int x1 = Math.min(highx, bboxx1); pix_minX = Math.min(pix_minX, x0 >> SUBPIXEL_LG_POSITIONS_X); pix_maxX = Math.max(pix_maxX, x1 >> SUBPIXEL_LG_POSITIONS_X); } int sum = 0; - int prev = boundsMinX; + int prev = bboxx0; for (int i = 0; i < numCrossings; i++) { int curxo = crossings[i]; int curx = curxo >> 1; int crorientation = ((curxo & 0x1) == 0x1) ? 1 : -1; if ((sum & mask) != 0) { - int x0 = Math.max(prev, boundsMinX); - int x1 = Math.min(curx, boundsMaxX); + int x0 = Math.max(prev, bboxx0); + int x1 = Math.min(curx, bboxx1); if (x0 < x1) { - x0 -= boundsMinX; // turn x0, x1 from coords to indeces - x1 -= boundsMinX; // in the alpha array. + x0 -= bboxx0; // turn x0, x1 from coords to indeces + x1 -= bboxx0; // in the alpha array. int pix_x = x0 >> SUBPIXEL_LG_POSITIONS_X; int pix_xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X; @@ -442,6 +794,9 @@ prev = curx; } + // even if this last row had no crossings, alpha will be zeroed + // from the last emitRow call. But this doesn't matter because + // maxX < minX, so no row will be emitted to the cache. if ((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) { emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX); pix_minX = Integer.MAX_VALUE; @@ -453,47 +808,53 @@ if (pix_maxX >= pix_minX) { emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX); } - pix_bboxX0 = minX >> SUBPIXEL_LG_POSITIONS_X; - pix_bboxX1 = maxX >> SUBPIXEL_LG_POSITIONS_X; - pix_bboxY0 = minY >> SUBPIXEL_LG_POSITIONS_Y; - pix_bboxY1 = maxY >> SUBPIXEL_LG_POSITIONS_Y; } + public void endRendering() { + final int bminx = boundsMinX >> SUBPIXEL_LG_POSITIONS_X; + final int bmaxx = boundsMaxX >> SUBPIXEL_LG_POSITIONS_X; + final int bminy = boundsMinY >> SUBPIXEL_LG_POSITIONS_Y; + final int bmaxy = boundsMaxY >> SUBPIXEL_LG_POSITIONS_Y; + final int eminx = ((int)Math.floor(edgeMinX)) >> SUBPIXEL_LG_POSITIONS_X; + final int emaxx = ((int)Math.ceil(edgeMaxX)) >> SUBPIXEL_LG_POSITIONS_X; + final int eminy = ((int)Math.floor(edgeMinY)) >> SUBPIXEL_LG_POSITIONS_Y; + final int emaxy = ((int)Math.ceil(edgeMaxY)) >> SUBPIXEL_LG_POSITIONS_Y; - public void endRendering() { - // Set up the cache to accumulate the bounding box - if (cache != null) { - cache.bboxX0 = Integer.MAX_VALUE; - cache.bboxY0 = Integer.MAX_VALUE; - cache.bboxX1 = Integer.MIN_VALUE; - cache.bboxY1 = Integer.MIN_VALUE; + final int minX = Math.max(bminx, eminx); + final int maxX = Math.min(bmaxx, emaxx); + final int minY = Math.max(bminy, eminy); + final int maxY = Math.min(bmaxy, emaxy); + if (minX > maxX || minY > maxY) { + this.cache = new PiscesCache(bminx, bminy, bmaxx, bmaxy); + return; } - _endRendering(); + this.cache = new PiscesCache(minX, minY, maxX, maxY); + _endRendering(minX, minY, maxX, maxY); } - public void getBoundingBox(int[] pix_bbox) { - pix_bbox[0] = pix_bboxX0; - pix_bbox[1] = pix_bboxY0; - pix_bbox[2] = pix_bboxX1 - pix_bboxX0; - pix_bbox[3] = pix_bboxY1 - pix_bboxY0; + public PiscesCache getCache() { + if (cache == null) { + throw new InternalError("cache not yet initialized"); + } + return cache; } - private void emitRow(byte[] alphaRow, int pix_y, int pix_from, int pix_to) { + private void emitRow(int[] alphaRow, int pix_y, int pix_from, int pix_to) { // Copy rowAA data into the cache if one is present if (cache != null) { if (pix_to >= pix_from) { - cache.startRow(pix_y, pix_from, pix_to); + cache.startRow(pix_y, pix_from); // Perform run-length encoding and store results in the cache - int from = pix_from - (boundsMinX >> SUBPIXEL_LG_POSITIONS_X); - int to = pix_to - (boundsMinX >> SUBPIXEL_LG_POSITIONS_X); + int from = pix_from - cache.bboxX0; + int to = pix_to - cache.bboxX0; int runLen = 1; - byte startVal = alphaRow[from]; + int startVal = alphaRow[from]; for (int i = from + 1; i <= to; i++) { - byte nextVal = (byte)(startVal + alphaRow[i]); - if (nextVal == startVal && runLen < 255) { + int nextVal = startVal + alphaRow[i]; + if (nextVal == startVal) { runLen++; } else { cache.addRLERun(startVal, runLen); @@ -502,9 +863,8 @@ } } cache.addRLERun(startVal, runLen); - cache.addRLERun((byte)0, 0); } } - java.util.Arrays.fill(alphaRow, (byte)0); + java.util.Arrays.fill(alphaRow, 0); } }
--- a/jdk/src/share/classes/sun/java2d/pisces/Stroker.java Tue Nov 09 11:45:00 2010 -0800 +++ b/jdk/src/share/classes/sun/java2d/pisces/Stroker.java Tue Nov 09 22:53:18 2010 -0800 @@ -25,10 +25,18 @@ package sun.java2d.pisces; -public class Stroker implements LineSink { +import java.util.Arrays; +import java.util.Iterator; + +import sun.awt.geom.PathConsumer2D; + +// TODO: some of the arithmetic here is too verbose and prone to hard to +// debug typos. We should consider making a small Point/Vector class that +// has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such +public class Stroker implements PathConsumer2D { private static final int MOVE_TO = 0; - private static final int LINE_TO = 1; + private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad private static final int CLOSE = 2; /** @@ -61,57 +69,37 @@ */ public static final int CAP_SQUARE = 2; - private final LineSink output; + private final PathConsumer2D out; private final int capStyle; private final int joinStyle; - private final float m00, m01, m10, m11, det; + private final float lineWidth2; - private final float lineWidth2; - private final float scaledLineWidth2; - - // For any pen offset (pen_dx, pen_dy) that does not depend on - // the line orientation, the pen should be transformed so that: - // - // pen_dx' = m00*pen_dx + m01*pen_dy - // pen_dy' = m10*pen_dx + m11*pen_dy - // - // For a round pen, this means: - // - // pen_dx(r, theta) = r*cos(theta) - // pen_dy(r, theta) = r*sin(theta) - // - // pen_dx'(r, theta) = r*(m00*cos(theta) + m01*sin(theta)) - // pen_dy'(r, theta) = r*(m10*cos(theta) + m11*sin(theta)) - private int numPenSegments; - private final float[] pen_dx; - private final float[] pen_dy; - private boolean[] penIncluded; - private final float[] join; - - private final float[] offset = new float[2]; - private float[] reverse = new float[100]; + private final float[][] offset = new float[3][2]; private final float[] miter = new float[2]; private final float miterLimitSq; private int prev; - private int rindex; - private boolean started; - private boolean lineToOrigin; - private boolean joinToOrigin; - private float sx0, sy0, sx1, sy1, x0, y0, px0, py0; - private float mx0, my0, omx, omy; + // The starting point of the path, and the slope there. + private float sx0, sy0, sdx, sdy; + // the current point and the slope there. + private float cx0, cy0, cdx, cdy; // c stands for current + // vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the + // first and last points on the left parallel path. Since this path is + // parallel, it's slope at any point is parallel to the slope of the + // original path (thought they may have different directions), so these + // could be computed from sdx,sdy and cdx,cdy (and vice versa), but that + // would be error prone and hard to read, so we keep these anyway. + private float smx, smy, cmx, cmy; - private float m00_2_m01_2; - private float m10_2_m11_2; - private float m00_m10_m01_m11; + private final PolyStack reverse = new PolyStack(); /** * Constructs a <code>Stroker</code>. * - * @param output an output <code>LineSink</code>. + * @param pc2d an output <code>PathConsumer2D</code>. * @param lineWidth the desired line width in pixels * @param capStyle the desired end cap style, one of * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or @@ -120,183 +108,61 @@ * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or * <code>JOIN_BEVEL</code>. * @param miterLimit the desired miter limit - * @param transform a <code>Transform4</code> object indicating - * the transform that has been previously applied to all incoming - * coordinates. This is required in order to produce consistently - * shaped end caps and joins. */ - public Stroker(LineSink output, + public Stroker(PathConsumer2D pc2d, float lineWidth, int capStyle, int joinStyle, - float miterLimit, - float m00, float m01, float m10, float m11) { - this.output = output; + float miterLimit) + { + this.out = pc2d; this.lineWidth2 = lineWidth / 2; - this.scaledLineWidth2 = m00 * lineWidth2; this.capStyle = capStyle; this.joinStyle = joinStyle; - m00_2_m01_2 = m00*m00 + m01*m01; - m10_2_m11_2 = m10*m10 + m11*m11; - m00_m10_m01_m11 = m00*m10 + m01*m11; - - this.m00 = m00; - this.m01 = m01; - this.m10 = m10; - this.m11 = m11; - det = m00*m11 - m01*m10; - - float limit = miterLimit * lineWidth2 * det; + float limit = miterLimit * lineWidth2; this.miterLimitSq = limit*limit; - this.numPenSegments = (int)(3.14159f * lineWidth); - this.pen_dx = new float[numPenSegments]; - this.pen_dy = new float[numPenSegments]; - this.penIncluded = new boolean[numPenSegments]; - this.join = new float[2*numPenSegments]; - - for (int i = 0; i < numPenSegments; i++) { - double theta = (i * 2.0 * Math.PI)/numPenSegments; - - double cos = Math.cos(theta); - double sin = Math.sin(theta); - pen_dx[i] = (float)(lineWidth2 * (m00*cos + m01*sin)); - pen_dy[i] = (float)(lineWidth2 * (m10*cos + m11*sin)); - } - - prev = CLOSE; - rindex = 0; - started = false; - lineToOrigin = false; + this.prev = CLOSE; } - private void computeOffset(float x0, float y0, - float x1, float y1, float[] m) { - float lx = x1 - x0; - float ly = y1 - y0; - - float dx, dy; - if (m00 > 0 && m00 == m11 && m01 == 0 & m10 == 0) { - float ilen = (float)Math.hypot(lx, ly); - if (ilen == 0) { - dx = dy = 0; - } else { - dx = (ly * scaledLineWidth2)/ilen; - dy = -(lx * scaledLineWidth2)/ilen; - } + private static void computeOffset(final float lx, final float ly, + final float w, final float[] m) + { + final float len = (float)Math.hypot(lx, ly); + if (len == 0) { + m[0] = m[1] = 0; } else { - int sdet = (det > 0) ? 1 : -1; - float a = ly * m00 - lx * m10; - float b = ly * m01 - lx * m11; - float dh = (float)Math.hypot(a, b); - float div = sdet * lineWidth2/dh; - - float ddx = ly * m00_2_m01_2 - lx * m00_m10_m01_m11; - float ddy = ly * m00_m10_m01_m11 - lx * m10_2_m11_2; - dx = ddx*div; - dy = ddy*div; - } - - m[0] = dx; - m[1] = dy; - } - - private void ensureCapacity(int newrindex) { - if (reverse.length < newrindex) { - reverse = java.util.Arrays.copyOf(reverse, 6*reverse.length/5); + m[0] = (ly * w)/len; + m[1] = -(lx * w)/len; } } - private boolean isCCW(float x0, float y0, - float x1, float y1, - float x2, float y2) { - return (x1 - x0) * (y2 - y1) < (y1 - y0) * (x2 - x1); - } - - private boolean side(float x, float y, - float x0, float y0, - float x1, float y1) { - return (y0 - y1)*x + (x1 - x0)*y + (x0*y1 - x1*y0) > 0; - } - - private int computeRoundJoin(float cx, float cy, - float xa, float ya, - float xb, float yb, - int side, - boolean flip, - float[] join) { - float px, py; - int ncoords = 0; - - boolean centerSide; - if (side == 0) { - centerSide = side(cx, cy, xa, ya, xb, yb); - } else { - centerSide = (side == 1); - } - for (int i = 0; i < numPenSegments; i++) { - px = cx + pen_dx[i]; - py = cy + pen_dy[i]; - - boolean penSide = side(px, py, xa, ya, xb, yb); - penIncluded[i] = (penSide != centerSide); - } - - int start = -1, end = -1; - for (int i = 0; i < numPenSegments; i++) { - if (penIncluded[i] && - !penIncluded[(i + numPenSegments - 1) % numPenSegments]) { - start = i; - } - if (penIncluded[i] && - !penIncluded[(i + 1) % numPenSegments]) { - end = i; - } - } - - if (end < start) { - end += numPenSegments; - } - - if (start != -1 && end != -1) { - float dxa = cx + pen_dx[start] - xa; - float dya = cy + pen_dy[start] - ya; - float dxb = cx + pen_dx[start] - xb; - float dyb = cy + pen_dy[start] - yb; - - boolean rev = (dxa*dxa + dya*dya > dxb*dxb + dyb*dyb); - int i = rev ? end : start; - int incr = rev ? -1 : 1; - while (true) { - int idx = i % numPenSegments; - px = cx + pen_dx[idx]; - py = cy + pen_dy[idx]; - join[ncoords++] = px; - join[ncoords++] = py; - if (i == (rev ? start : end)) { - break; - } - i += incr; - } - } - - return ncoords/2; + // Returns true if the vectors (dx1, dy1) and (dx2, dy2) are + // clockwise (if dx1,dy1 needs to be rotated clockwise to close + // the smallest angle between it and dx2,dy2). + // This is equivalent to detecting whether a point q is on the right side + // of a line passing through points p1, p2 where p2 = p1+(dx1,dy1) and + // q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a + // clockwise order. + // NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left. + private static boolean isCW(final float dx1, final float dy1, + final float dx2, final float dy2) + { + return dx1 * dy2 <= dy1 * dx2; } // pisces used to use fixed point arithmetic with 16 decimal digits. I - // didn't want to change the values of the constants below when I converted + // didn't want to change the values of the constant below when I converted // it to floating point, so that's why the divisions by 2^16 are there. private static final float ROUND_JOIN_THRESHOLD = 1000/65536f; - private static final float ROUND_JOIN_INTERNAL_THRESHOLD = 1000000000/65536f; private void drawRoundJoin(float x, float y, float omx, float omy, float mx, float my, - int side, - boolean flip, boolean rev, - float threshold) { + float threshold) + { if ((omx == 0 && omy == 0) || (mx == 0 && my == 0)) { return; } @@ -314,54 +180,148 @@ mx = -mx; my = -my; } + drawRoundJoin(x, y, omx, omy, mx, my, rev); + } - float bx0 = x + omx; - float by0 = y + omy; - float bx1 = x + mx; - float by1 = y + my; + private void drawRoundJoin(float cx, float cy, + float omx, float omy, + float mx, float my, + boolean rev) + { + // The sign of the dot product of mx,my and omx,omy is equal to the + // the sign of the cosine of ext + // (ext is the angle between omx,omy and mx,my). + double cosext = omx * mx + omy * my; + // If it is >=0, we know that abs(ext) is <= 90 degrees, so we only + // need 1 curve to approximate the circle section that joins omx,omy + // and mx,my. + final int numCurves = cosext >= 0 ? 1 : 2; - int npoints = computeRoundJoin(x, y, - bx0, by0, bx1, by1, side, flip, - join); - for (int i = 0; i < npoints; i++) { - emitLineTo(join[2*i], join[2*i + 1], rev); + switch (numCurves) { + case 1: + drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev); + break; + case 2: + // we need to split the arc into 2 arcs spanning the same angle. + // The point we want will be one of the 2 intersections of the + // perpendicular bisector of the chord (omx,omy)->(mx,my) and the + // circle. We could find this by scaling the vector + // (omx+mx, omy+my)/2 so that it has length=lineWidth2 (and thus lies + // on the circle), but that can have numerical problems when the angle + // between omx,omy and mx,my is close to 180 degrees. So we compute a + // normal of (omx,omy)-(mx,my). This will be the direction of the + // perpendicular bisector. To get one of the intersections, we just scale + // this vector that its length is lineWidth2 (this works because the + // perpendicular bisector goes through the origin). This scaling doesn't + // have numerical problems because we know that lineWidth2 divided by + // this normal's length is at least 0.5 and at most sqrt(2)/2 (because + // we know the angle of the arc is > 90 degrees). + float nx = my - omy, ny = omx - mx; + float nlen = (float)Math.sqrt(nx*nx + ny*ny); + float scale = lineWidth2/nlen; + float mmx = nx * scale, mmy = ny * scale; + + // if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've + // computed the wrong intersection so we get the other one. + // The test above is equivalent to if (rev). + if (rev) { + mmx = -mmx; + mmy = -mmy; + } + drawBezApproxForArc(cx, cy, omx, omy, mmx, mmy, rev); + drawBezApproxForArc(cx, cy, mmx, mmy, mx, my, rev); + break; } } - // Return the intersection point of the lines (ix0, iy0) -> (ix1, iy1) - // and (ix0p, iy0p) -> (ix1p, iy1p) in m[0] and m[1] - private void computeMiter(float x0, float y0, float x1, float y1, - float x0p, float y0p, float x1p, float y1p, - float[] m) { + // the input arc defined by omx,omy and mx,my must span <= 90 degrees. + private void drawBezApproxForArc(final float cx, final float cy, + final float omx, final float omy, + final float mx, final float my, + boolean rev) + { + float cosext2 = (omx * mx + omy * my) / (2 * lineWidth2 * lineWidth2); + // cv is the length of P1-P0 and P2-P3 divided by the radius of the arc + // (so, cv assumes the arc has radius 1). P0, P1, P2, P3 are the points that + // define the bezier curve we're computing. + // It is computed using the constraints that P1-P0 and P3-P2 are parallel + // to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|. + float cv = (float)((4.0 / 3.0) * Math.sqrt(0.5-cosext2) / + (1.0 + Math.sqrt(cosext2+0.5))); + // if clockwise, we need to negate cv. + if (rev) { // rev is equivalent to isCW(omx, omy, mx, my) + cv = -cv; + } + final float x1 = cx + omx; + final float y1 = cy + omy; + final float x2 = x1 - cv * omy; + final float y2 = y1 + cv * omx; + + final float x4 = cx + mx; + final float y4 = cy + my; + final float x3 = x4 + cv * my; + final float y3 = y4 - cv * mx; + + emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev); + } + + private void drawRoundCap(float cx, float cy, float mx, float my) { + final float C = 0.5522847498307933f; + // the first and second arguments of the following two calls + // are really will be ignored by emitCurveTo (because of the false), + // but we put them in anyway, as opposed to just giving it 4 zeroes, + // because it's just 4 additions and it's not good to rely on this + // sort of assumption (right now it's true, but that may change). + emitCurveTo(cx+mx, cy+my, + cx+mx-C*my, cy+my+C*mx, + cx-my+C*mx, cy+mx+C*my, + cx-my, cy+mx, + false); + emitCurveTo(cx-my, cy+mx, + cx-my-C*mx, cy+mx-C*my, + cx-mx-C*my, cy-my+C*mx, + cx-mx, cy-my, + false); + } + + // Return the intersection point of the lines (x0, y0) -> (x1, y1) + // and (x0p, y0p) -> (x1p, y1p) in m[0] and m[1] + private void computeMiter(final float x0, final float y0, + final float x1, final float y1, + final float x0p, final float y0p, + final float x1p, final float y1p, + final float[] m, int off) + { float x10 = x1 - x0; float y10 = y1 - y0; float x10p = x1p - x0p; float y10p = y1p - y0p; + // if this is 0, the lines are parallel. If they go in the + // same direction, there is no intersection so m[off] and + // m[off+1] will contain infinity, so no miter will be drawn. + // If they go in the same direction that means that the start of the + // current segment and the end of the previous segment have the same + // tangent, in which case this method won't even be involved in + // miter drawing because it won't be called by drawMiter (because + // (mx == omx && my == omy) will be true, and drawMiter will return + // immediately). float den = x10*y10p - x10p*y10; - if (den == 0) { - m[0] = x0; - m[1] = y0; - return; - } - - float t = x1p*(y0 - y0p) - x0*y10p + x0p*(y1p - y0); - m[0] = x0 + (t*x10)/den; - m[1] = y0 + (t*y10)/den; + float t = x10p*(y0-y0p) - y10p*(x0-x0p); + t /= den; + m[off++] = x0 + t*x10; + m[off] = y0 + t*y10; } - private void drawMiter(float px0, float py0, - float x0, float y0, - float x1, float y1, + private void drawMiter(final float pdx, final float pdy, + final float x0, final float y0, + final float dx, final float dy, float omx, float omy, float mx, float my, - boolean rev) { - if (mx == omx && my == omy) { - return; - } - if (px0 == x0 && py0 == y0) { - return; - } - if (x0 == x1 && y0 == y1) { + boolean rev) + { + if ((mx == omx && my == omy) || + (pdx == 0 && pdy == 0) || + (dx == 0 && dy == 0)) { return; } @@ -372,297 +332,734 @@ my = -my; } - computeMiter(px0 + omx, py0 + omy, x0 + omx, y0 + omy, - x0 + mx, y0 + my, x1 + mx, y1 + my, - miter); + computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy, + (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, + miter, 0); - // Compute miter length in untransformed coordinates - float dx = miter[0] - x0; - float dy = miter[1] - y0; - float a = dy*m00 - dx*m10; - float b = dy*m01 - dx*m11; - float lenSq = a*a + b*b; + float lenSq = (miter[0]-x0)*(miter[0]-x0) + (miter[1]-y0)*(miter[1]-y0); if (lenSq < miterLimitSq) { emitLineTo(miter[0], miter[1], rev); } } - public void moveTo(float x0, float y0) { - // System.out.println("Stroker.moveTo(" + x0/65536.0 + ", " + y0/65536.0 + ")"); - - if (lineToOrigin) { - // not closing the path, do the previous lineTo - lineToImpl(sx0, sy0, joinToOrigin);