From 6ce08d09859e30ed31e4e3b990695f644b8827c1 Mon Sep 17 00:00:00 2001 From: Masayoshi Okutsu Date: Tue, 25 Oct 2016 15:43:19 +0900 Subject: [PATCH] 8165804: Revisit the way of loading BreakIterator rules/dictionaries Reviewed-by: naoto, peytoia, erikj --- jdk/make/gendata/GendataBreakIterator.gmk | 1 - .../provider => text}/BreakDictionary.java | 160 ++++++--------- .../DictionaryBasedBreakIterator.java | 24 ++- .../RuleBasedBreakIterator.java | 192 +++++++----------- .../resources/BreakIteratorResources.java | 36 ++++ .../provider/BreakIteratorProviderImpl.java | 24 ++- .../util/locale/provider/LocaleResources.java | 10 +- .../BreakIteratorResourceBundle.java | 114 +++++++++++ .../sun/util/resources/LocaleData.java | 8 + .../ext/BreakIteratorResources_th.java | 36 ++++ .../BreakIteratorProviderTest.java | 4 +- .../BreakIteratorProviderTest.sh | 4 +- .../plugins/IncludeLocalesPluginTest.java | 4 +- 13 files changed, 359 insertions(+), 258 deletions(-) rename jdk/src/java.base/share/classes/sun/{util/locale/provider => text}/BreakDictionary.java (70%) rename jdk/src/java.base/share/classes/sun/{util/locale/provider => text}/DictionaryBasedBreakIterator.java (96%) rename jdk/src/java.base/share/classes/sun/{util/locale/provider => text}/RuleBasedBreakIterator.java (88%) create mode 100644 jdk/src/java.base/share/classes/sun/text/resources/BreakIteratorResources.java create mode 100644 jdk/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java create mode 100644 jdk/src/jdk.localedata/share/classes/sun/text/resources/ext/BreakIteratorResources_th.java diff --git a/jdk/make/gendata/GendataBreakIterator.gmk b/jdk/make/gendata/GendataBreakIterator.gmk index 828e6314790..39a5dfb5efc 100644 --- a/jdk/make/gendata/GendataBreakIterator.gmk +++ b/jdk/make/gendata/GendataBreakIterator.gmk @@ -55,7 +55,6 @@ $(eval $(call SetupJavaCompilation,BUILD_BREAKITERATOR_BASE, \ $(eval $(call SetupJavaCompilation,BUILD_BREAKITERATOR_LD, \ SETUP := GENERATE_OLDBYTECODE, \ SRC := $(JDK_TOPDIR)/src/jdk.localedata/share/classes, \ - INCLUDES := $(TEXT_PKG_LD), \ INCLUDE_FILES := \ $(TEXT_PKG_LD)/BreakIteratorRules_th.java \ $(TEXT_PKG_LD)/BreakIteratorInfo_th.java, \ diff --git a/jdk/src/java.base/share/classes/sun/util/locale/provider/BreakDictionary.java b/jdk/src/java.base/share/classes/sun/text/BreakDictionary.java similarity index 70% rename from jdk/src/java.base/share/classes/sun/util/locale/provider/BreakDictionary.java rename to jdk/src/java.base/share/classes/sun/text/BreakDictionary.java index 4be1363b58f..ae2aba1e7c2 100644 --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/BreakDictionary.java +++ b/jdk/src/java.base/share/classes/sun/text/BreakDictionary.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2016, 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,15 +37,10 @@ * This notice and attribution to Taligent may not be removed. * Taligent is a registered trademark of Taligent, Inc. */ -package sun.util.locale.provider; +package sun.text; -import java.io.BufferedInputStream; -import java.io.InputStream; -import java.io.IOException; -import java.lang.reflect.Module; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; import java.util.MissingResourceException; import sun.text.CompactByteArray; import sun.text.SupplementaryCharacterData; @@ -137,131 +132,90 @@ class BreakDictionary { // deserialization //========================================================================= - BreakDictionary(Module module, String dictionaryName) - throws IOException, MissingResourceException { - - readDictionaryFile(module, dictionaryName); + BreakDictionary(String dictionaryName, byte[] dictionaryData) { + try { + setupDictionary(dictionaryName, dictionaryData); + } catch (BufferUnderflowException bue) { + MissingResourceException e; + e = new MissingResourceException("Corrupted dictionary data", + dictionaryName, ""); + e.initCause(bue); + throw e; + } } - private void readDictionaryFile(final Module module, final String dictionaryName) - throws IOException, MissingResourceException { - - BufferedInputStream in; - try { - PrivilegedExceptionAction pa = () -> { - String pathName = "jdk.localedata".equals(module.getName()) ? - "sun/text/resources/ext/" : - "sun/text/resources/"; - InputStream is = module.getResourceAsStream(pathName + dictionaryName); - if (is == null) { - // Try to load the file with "java.base" module instance. Assumption - // here is that the fall back data files to be read should reside in - // java.base. - is = BreakDictionary.class.getModule().getResourceAsStream("sun/text/resources/" + dictionaryName); - } - - return new BufferedInputStream(is); - }; - in = AccessController.doPrivileged(pa); - } - catch (PrivilegedActionException e) { - throw new InternalError(e.toString(), e); - } - - byte[] buf = new byte[8]; - if (in.read(buf) != 8) { - throw new MissingResourceException("Wrong data length", - dictionaryName, ""); - } + private void setupDictionary(String dictionaryName, byte[] dictionaryData) { + ByteBuffer bb = ByteBuffer.wrap(dictionaryData); // check version - int version = RuleBasedBreakIterator.getInt(buf, 0); + int version = bb.getInt(); if (version != supportedVersion) { throw new MissingResourceException("Dictionary version(" + version + ") is unsupported", - dictionaryName, ""); - } - - // get data size - int len = RuleBasedBreakIterator.getInt(buf, 4); - buf = new byte[len]; - if (in.read(buf) != len) { - throw new MissingResourceException("Wrong data length", dictionaryName, ""); } - // close the stream - in.close(); - - int l; - int offset = 0; + // Check data size + int len = bb.getInt(); + if (bb.position() + len != bb.limit()) { + throw new MissingResourceException("Dictionary size is wrong: " + bb.limit(), + dictionaryName, ""); + } // read in the column map for BMP characteres (this is serialized in // its internal form: an index array followed by a data array) - l = RuleBasedBreakIterator.getInt(buf, offset); - offset += 4; - short[] temp = new short[l]; - for (int i = 0; i < l; i++, offset+=2) { - temp[i] = RuleBasedBreakIterator.getShort(buf, offset); - } - l = RuleBasedBreakIterator.getInt(buf, offset); - offset += 4; - byte[] temp2 = new byte[l]; - for (int i = 0; i < l; i++, offset++) { - temp2[i] = buf[offset]; + len = bb.getInt(); + short[] temp = new short[len]; + for (int i = 0; i < len; i++) { + temp[i] = bb.getShort(); } + len = bb.getInt(); + byte[] temp2 = new byte[len]; + bb.get(temp2); columnMap = new CompactByteArray(temp, temp2); // read in numCols and numColGroups - numCols = RuleBasedBreakIterator.getInt(buf, offset); - offset += 4; - numColGroups = RuleBasedBreakIterator.getInt(buf, offset); - offset += 4; + numCols = bb.getInt(); + numColGroups = bb.getInt(); // read in the row-number index - l = RuleBasedBreakIterator.getInt(buf, offset); - offset += 4; - rowIndex = new short[l]; - for (int i = 0; i < l; i++, offset+=2) { - rowIndex[i] = RuleBasedBreakIterator.getShort(buf, offset); + len = bb.getInt(); + rowIndex = new short[len]; + for (int i = 0; i < len; i++) { + rowIndex[i] = bb.getShort(); } // load in the populated-cells bitmap: index first, then bitmap list - l = RuleBasedBreakIterator.getInt(buf, offset); - offset += 4; - rowIndexFlagsIndex = new short[l]; - for (int i = 0; i < l; i++, offset+=2) { - rowIndexFlagsIndex[i] = RuleBasedBreakIterator.getShort(buf, offset); + len = bb.getInt(); + rowIndexFlagsIndex = new short[len]; + for (int i = 0; i < len; i++) { + rowIndexFlagsIndex[i] = bb.getShort(); } - l = RuleBasedBreakIterator.getInt(buf, offset); - offset += 4; - rowIndexFlags = new int[l]; - for (int i = 0; i < l; i++, offset+=4) { - rowIndexFlags[i] = RuleBasedBreakIterator.getInt(buf, offset); + len = bb.getInt(); + rowIndexFlags = new int[len]; + for (int i = 0; i < len; i++) { + rowIndexFlags[i] = bb.getInt(); } // load in the row-shift index - l = RuleBasedBreakIterator.getInt(buf, offset); - offset += 4; - rowIndexShifts = new byte[l]; - for (int i = 0; i < l; i++, offset++) { - rowIndexShifts[i] = buf[offset]; - } + len = bb.getInt(); + rowIndexShifts = new byte[len]; + bb.get(rowIndexShifts); // load in the actual state table - l = RuleBasedBreakIterator.getInt(buf, offset); - offset += 4; - table = new short[l]; - for (int i = 0; i < l; i++, offset+=2) { - table[i] = RuleBasedBreakIterator.getShort(buf, offset); + len = bb.getInt(); + table = new short[len]; + for (int i = 0; i < len; i++) { + table[i] = bb.getShort(); } // finally, prepare the column map for supplementary characters - l = RuleBasedBreakIterator.getInt(buf, offset); - offset += 4; - int[] temp3 = new int[l]; - for (int i = 0; i < l; i++, offset+=4) { - temp3[i] = RuleBasedBreakIterator.getInt(buf, offset); + len = bb.getInt(); + int[] temp3 = new int[len]; + for (int i = 0; i < len; i++) { + temp3[i] = bb.getInt(); } + assert bb.position() == bb.limit(); + supplementaryCharColumnMap = new SupplementaryCharacterData(temp3); } diff --git a/jdk/src/java.base/share/classes/sun/util/locale/provider/DictionaryBasedBreakIterator.java b/jdk/src/java.base/share/classes/sun/text/DictionaryBasedBreakIterator.java similarity index 96% rename from jdk/src/java.base/share/classes/sun/util/locale/provider/DictionaryBasedBreakIterator.java rename to jdk/src/java.base/share/classes/sun/text/DictionaryBasedBreakIterator.java index f7eff4e80cf..bd47872bec1 100644 --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/DictionaryBasedBreakIterator.java +++ b/jdk/src/java.base/share/classes/sun/text/DictionaryBasedBreakIterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2016, 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 @@ -38,10 +38,8 @@ * Taligent is a registered trademark of Taligent, Inc. */ -package sun.util.locale.provider; +package sun.text; -import java.io.IOException; -import java.lang.reflect.Module; import java.text.CharacterIterator; import java.util.ArrayList; import java.util.List; @@ -72,7 +70,7 @@ import java.util.Stack; * slow) BuildDictionaryFile utility for creating dictionary files, but aren't * currently making it public. Contact us for help. */ -class DictionaryBasedBreakIterator extends RuleBasedBreakIterator { +public class DictionaryBasedBreakIterator extends RuleBasedBreakIterator { /** * a list of known words that is used to divide up contiguous ranges of letters, @@ -109,18 +107,22 @@ class DictionaryBasedBreakIterator extends RuleBasedBreakIterator { /** * Constructs a DictionaryBasedBreakIterator. - * @param module The module where the dictionary file resides - * @param dictionaryFilename The filename of the dictionary file to use + * + * @param ruleFile the name of the rule data file + * @param ruleData the rule data loaded from the rule data file + * @param dictionaryFile the name of the dictionary file + * @param dictionartData the dictionary data loaded from the dictionary file + * @throws MissingResourceException if rule data or dictionary initialization failed */ - DictionaryBasedBreakIterator(Module module, String dataFile, String dictionaryFile) - throws IOException { - super(module, dataFile); + public DictionaryBasedBreakIterator(String ruleFile, byte[] ruleData, + String dictionaryFile, byte[] dictionaryData) { + super(ruleFile, ruleData); byte[] tmp = super.getAdditionalData(); if (tmp != null) { prepareCategoryFlags(tmp); super.setAdditionalData(null); } - dictionary = new BreakDictionary(module, dictionaryFile); + dictionary = new BreakDictionary(dictionaryFile, dictionaryData); } private void prepareCategoryFlags(byte[] data) { diff --git a/jdk/src/java.base/share/classes/sun/util/locale/provider/RuleBasedBreakIterator.java b/jdk/src/java.base/share/classes/sun/text/RuleBasedBreakIterator.java similarity index 88% rename from jdk/src/java.base/share/classes/sun/util/locale/provider/RuleBasedBreakIterator.java rename to jdk/src/java.base/share/classes/sun/text/RuleBasedBreakIterator.java index ebf6ba1956e..b3abf812f29 100644 --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/RuleBasedBreakIterator.java +++ b/jdk/src/java.base/share/classes/sun/text/RuleBasedBreakIterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2016, 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 @@ -38,15 +38,10 @@ * Taligent is a registered trademark of Taligent, Inc. */ -package sun.util.locale.provider; +package sun.text; -import java.io.BufferedInputStream; -import java.io.InputStream; -import java.io.IOException; -import java.lang.reflect.Module; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; import java.text.BreakIterator; import java.text.CharacterIterator; import java.text.StringCharacterIterator; @@ -218,7 +213,7 @@ import sun.text.SupplementaryCharacterData; * * @author Richard Gillam */ -class RuleBasedBreakIterator extends BreakIterator { +public class RuleBasedBreakIterator extends BreakIterator { /** * A token used as a character-category value to identify ignore characters @@ -249,11 +244,6 @@ class RuleBasedBreakIterator extends BreakIterator { */ static final byte supportedVersion = 1; - /** - * Header size in byte count - */ - private static final int HEADER_LENGTH = 36; - /** * An array length of indices for BMP characters */ @@ -315,16 +305,26 @@ class RuleBasedBreakIterator extends BreakIterator { //======================================================================= /** - * Constructs a RuleBasedBreakIterator according to the module and the datafile - * provided. + * Constructs a RuleBasedBreakIterator using the given rule data. + * + * @throws MissingResourceException if the rule data is invalid or corrupted */ - RuleBasedBreakIterator(Module module, String datafile) - throws IOException, MissingResourceException { - readTables(module, datafile); + public RuleBasedBreakIterator(String ruleFile, byte[] ruleData) { + ByteBuffer bb = ByteBuffer.wrap(ruleData); + try { + validateRuleData(ruleFile, bb); + setupTables(ruleFile, bb); + } catch (BufferUnderflowException bue) { + MissingResourceException e; + e = new MissingResourceException("Corrupted rule data file", ruleFile, ""); + e.initCause(bue); + throw e; + } } /** - * Read datafile. The datafile's format is as follows: + * Initializes the fields with the given rule data. + * The data format is as follows: *
      *   BreakIteratorData {
      *       u1           magic[7];
@@ -370,133 +370,101 @@ class RuleBasedBreakIterator extends BreakIterator {
      *       u1           additionalData[additionalDataLength];
      *   }
      * 
+ * + * @throws BufferUnderflowException if the end-of-data is reached before + * setting up all the tables */ - protected final void readTables(Module module, String datafile) - throws IOException, MissingResourceException { - - byte[] buffer = readFile(module, datafile); - + private void setupTables(String ruleFile, ByteBuffer bb) { /* Read header_info. */ - int stateTableLength = getInt(buffer, 0); - int backwardsStateTableLength = getInt(buffer, 4); - int endStatesLength = getInt(buffer, 8); - int lookaheadStatesLength = getInt(buffer, 12); - int BMPdataLength = getInt(buffer, 16); - int nonBMPdataLength = getInt(buffer, 20); - int additionalDataLength = getInt(buffer, 24); - checksum = getLong(buffer, 28); + int stateTableLength = bb.getInt(); + int backwardsStateTableLength = bb.getInt(); + int endStatesLength = bb.getInt(); + int lookaheadStatesLength = bb.getInt(); + int BMPdataLength = bb.getInt(); + int nonBMPdataLength = bb.getInt(); + int additionalDataLength = bb.getInt(); + checksum = bb.getLong(); /* Read stateTable[numCategories * numRows] */ stateTable = new short[stateTableLength]; - int offset = HEADER_LENGTH; - for (int i = 0; i < stateTableLength; i++, offset+=2) { - stateTable[i] = getShort(buffer, offset); + for (int i = 0; i < stateTableLength; i++) { + stateTable[i] = bb.getShort(); } /* Read backwardsStateTable[numCategories * numRows] */ backwardsStateTable = new short[backwardsStateTableLength]; - for (int i = 0; i < backwardsStateTableLength; i++, offset+=2) { - backwardsStateTable[i] = getShort(buffer, offset); + for (int i = 0; i < backwardsStateTableLength; i++) { + backwardsStateTable[i] = bb.getShort(); } /* Read endStates[numRows] */ endStates = new boolean[endStatesLength]; - for (int i = 0; i < endStatesLength; i++, offset++) { - endStates[i] = buffer[offset] == 1; + for (int i = 0; i < endStatesLength; i++) { + endStates[i] = bb.get() == 1; } /* Read lookaheadStates[numRows] */ lookaheadStates = new boolean[lookaheadStatesLength]; - for (int i = 0; i < lookaheadStatesLength; i++, offset++) { - lookaheadStates[i] = buffer[offset] == 1; + for (int i = 0; i < lookaheadStatesLength; i++) { + lookaheadStates[i] = bb.get() == 1; } /* Read a category table and indices for BMP characters. */ short[] temp1 = new short[BMP_INDICES_LENGTH]; // BMPindices - for (int i = 0; i < BMP_INDICES_LENGTH; i++, offset+=2) { - temp1[i] = getShort(buffer, offset); + for (int i = 0; i < BMP_INDICES_LENGTH; i++) { + temp1[i] = bb.getShort(); } byte[] temp2 = new byte[BMPdataLength]; // BMPdata - System.arraycopy(buffer, offset, temp2, 0, BMPdataLength); - offset += BMPdataLength; + bb.get(temp2); charCategoryTable = new CompactByteArray(temp1, temp2); /* Read a category table for non-BMP characters. */ int[] temp3 = new int[nonBMPdataLength]; - for (int i = 0; i < nonBMPdataLength; i++, offset+=4) { - temp3[i] = getInt(buffer, offset); + for (int i = 0; i < nonBMPdataLength; i++) { + temp3[i] = bb.getInt(); } supplementaryCharCategoryTable = new SupplementaryCharacterData(temp3); /* Read additional data */ if (additionalDataLength > 0) { additionalData = new byte[additionalDataLength]; - System.arraycopy(buffer, offset, additionalData, 0, additionalDataLength); + bb.get(additionalData); } + assert bb.position() == bb.limit(); /* Set numCategories */ numCategories = stateTable.length / endStates.length; } - protected byte[] readFile(final Module module, final String datafile) - throws IOException, MissingResourceException { - - BufferedInputStream is; - try { - PrivilegedExceptionAction pa = () -> { - String pathName = "jdk.localedata".equals(module.getName()) ? - "sun/text/resources/ext/" : - "sun/text/resources/"; - InputStream in = module.getResourceAsStream(pathName + datafile); - if (in == null) { - // Try to load the file with "java.base" module instance. Assumption - // here is that the fall back data files to be read should reside in - // java.base. - in = RuleBasedBreakIterator.class.getModule().getResourceAsStream("sun/text/resources/" + datafile); - } - - return new BufferedInputStream(in); - }; - is = AccessController.doPrivileged(pa); - } catch (PrivilegedActionException e) { - throw new InternalError(e.toString(), e); - } - - int offset = 0; - - /* First, read magic, version, and header_info. */ - int len = LABEL_LENGTH + 5; - byte[] buf = new byte[len]; - if (is.read(buf) != len) { - throw new MissingResourceException("Wrong header length", - datafile, ""); - } - - /* Validate the magic number. */ - for (int i = 0; i < LABEL_LENGTH; i++, offset++) { - if (buf[offset] != LABEL[offset]) { + /** + * Validates the magic number, version, and the length of the given data. + * + * @throws BufferUnderflowException if the end-of-data is reached while + * validating data + * @throws MissingResourceException if valification failed + */ + void validateRuleData(String ruleFile, ByteBuffer bb) { + /* Verify the magic number. */ + for (int i = 0; i < LABEL_LENGTH; i++) { + if (bb.get() != LABEL[i]) { throw new MissingResourceException("Wrong magic number", - datafile, ""); + ruleFile, ""); } } - /* Validate the version number. */ - if (buf[offset] != supportedVersion) { - throw new MissingResourceException("Unsupported version(" + buf[offset] + ")", - datafile, ""); + /* Verify the version number. */ + byte version = bb.get(); + if (version != supportedVersion) { + throw new MissingResourceException("Unsupported version(" + version + ")", + ruleFile, ""); } - /* Read data: totalDataSize + 8(for checksum) */ - len = getInt(buf, ++offset); - buf = new byte[len]; - if (is.read(buf) != len) { + // Check the length of the rest of data + int len = bb.getInt(); + if (bb.position() + len != bb.limit()) { throw new MissingResourceException("Wrong data length", - datafile, ""); + ruleFile, ""); } - - is.close(); - - return buf; } byte[] getAdditionalData() { @@ -1061,28 +1029,6 @@ class RuleBasedBreakIterator extends BreakIterator { return backwardsStateTable[state * numCategories + category]; } - static long getLong(byte[] buf, int offset) { - long num = buf[offset]&0xFF; - for (int i = 1; i < 8; i++) { - num = num<<8 | (buf[offset+i]&0xFF); - } - return num; - } - - static int getInt(byte[] buf, int offset) { - int num = buf[offset]&0xFF; - for (int i = 1; i < 4; i++) { - num = num<<8 | (buf[offset+i]&0xFF); - } - return num; - } - - static short getShort(byte[] buf, int offset) { - short num = (short)(buf[offset]&0xFF); - num = (short)(num<<8 | (buf[offset+1]&0xFF)); - return num; - } - /* * This class exists to work around a bug in incorrect implementations * of CharacterIterator, which incorrectly handle setIndex(endIndex). diff --git a/jdk/src/java.base/share/classes/sun/text/resources/BreakIteratorResources.java b/jdk/src/java.base/share/classes/sun/text/resources/BreakIteratorResources.java new file mode 100644 index 00000000000..848117e126e --- /dev/null +++ b/jdk/src/java.base/share/classes/sun/text/resources/BreakIteratorResources.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016, 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.text.resources; + +import java.util.ResourceBundle; +import sun.util.resources.BreakIteratorResourceBundle; + +public class BreakIteratorResources extends BreakIteratorResourceBundle { + @Override + protected ResourceBundle getBreakIteratorInfo() { + return new BreakIteratorInfo(); + } +} diff --git a/jdk/src/java.base/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java b/jdk/src/java.base/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java index 2de50a98962..1343dadb3b9 100644 --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java +++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2016, 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 @@ -32,6 +32,8 @@ import java.util.Locale; import java.util.MissingResourceException; import java.util.Objects; import java.util.Set; +import sun.text.DictionaryBasedBreakIterator; +import sun.text.RuleBasedBreakIterator; /** * Concrete implementation of the {@link java.text.spi.BreakIteratorProvider @@ -153,29 +155,31 @@ public class BreakIteratorProviderImpl extends BreakIteratorProvider } private BreakIterator getBreakInstance(Locale locale, - int type, - String dataName, - String dictionaryName) { + int type, + String ruleName, + String dictionaryName) { Objects.requireNonNull(locale); LocaleResources lr = LocaleProviderAdapter.forJRE().getLocaleResources(locale); String[] classNames = (String[]) lr.getBreakIteratorInfo("BreakIteratorClasses"); - String dataFile = (String) lr.getBreakIteratorInfo(dataName); + String ruleFile = (String) lr.getBreakIteratorInfo(ruleName); + byte[] ruleData = lr.getBreakIteratorResources(ruleName); try { switch (classNames[type]) { case "RuleBasedBreakIterator": - return new RuleBasedBreakIterator( - lr.getBreakIteratorDataModule(), dataFile); + return new RuleBasedBreakIterator(ruleFile, ruleData); + case "DictionaryBasedBreakIterator": String dictionaryFile = (String) lr.getBreakIteratorInfo(dictionaryName); - return new DictionaryBasedBreakIterator( - lr.getBreakIteratorDataModule(), dataFile, dictionaryFile); + byte[] dictionaryData = lr.getBreakIteratorResources(dictionaryName); + return new DictionaryBasedBreakIterator(ruleFile, ruleData, + dictionaryFile, dictionaryData); default: throw new IllegalArgumentException("Invalid break iterator class \"" + classNames[type] + "\""); } - } catch (IOException | MissingResourceException | IllegalArgumentException e) { + } catch (MissingResourceException | IllegalArgumentException e) { throw new InternalError(e.toString(), e); } } diff --git a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java index 2269d608eb5..a6f3bc53bb0 100644 --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java +++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, 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 @@ -42,7 +42,6 @@ package sun.util.locale.provider; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; -import java.lang.reflect.Module; import java.text.MessageFormat; import java.util.Calendar; import java.util.LinkedHashSet; @@ -113,13 +112,14 @@ public class LocaleResources { if (data == null || ((biInfo = data.get()) == null)) { biInfo = localeData.getBreakIteratorInfo(locale).getObject(key); cache.put(cacheKey, new ResourceReference(cacheKey, biInfo, referenceQueue)); - } + } return biInfo; } - Module getBreakIteratorDataModule() { - return localeData.getBreakIteratorInfo(locale).getClass().getModule(); + @SuppressWarnings("unchecked") + byte[] getBreakIteratorResources(String key) { + return (byte[]) localeData.getBreakIteratorResources(locale).getObject(key); } int getCalendarData(String key) { diff --git a/jdk/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java b/jdk/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java new file mode 100644 index 00000000000..d859e2a3ace --- /dev/null +++ b/jdk/src/java.base/share/classes/sun/util/resources/BreakIteratorResourceBundle.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016, 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.util.resources; + +import java.io.InputStream; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Collections; +import java.util.Enumeration; +import java.util.ResourceBundle; +import java.util.Set; + +/** + * BreakIteratorResourceBundle is an abstract class for loading BreakIterator + * data (rules or dictionary) from each module. An implementation class must + * implement getBreakIteratorInfo() that returns an instance of the + * corresponding BreakIteratorInfo (basename). The data name is taken from the + * BreakIteratorInfo instance. + * + *

For example, if the given key is "WordDictionary" and Locale is "th", the + * data name is taken from a BreakIteratorInfo_th and the key's value is + * "thai_dict". Its data thai_dict is loaded from the Module of the + * implementation class of this class. + */ + +public abstract class BreakIteratorResourceBundle extends ResourceBundle { + // If any keys that are not for data names are added to BreakIteratorInfo*, + // those keys must be added to NON_DATA_KEYS. + private static final Set NON_DATA_KEYS = Set.of("BreakIteratorClasses"); + + private volatile Set keys; + + /** + * Returns an instance of the corresponding {@code BreakIteratorInfo} (basename). + * The instance shouldn't have its parent. + */ + protected abstract ResourceBundle getBreakIteratorInfo(); + + @Override + protected Object handleGetObject(String key) { + if (NON_DATA_KEYS.contains(key)) { + return null; + } + ResourceBundle info = getBreakIteratorInfo(); + if (!info.containsKey(key)) { + return null; + } + String path = getClass().getPackage().getName().replace('.', '/') + + '/' + info.getString(key); + byte[] data; + try (InputStream is = getResourceAsStream(path)) { + data = is.readAllBytes(); + } catch (Exception e) { + throw new InternalError("Can't load " + path, e); + } + return data; + } + + private InputStream getResourceAsStream(String path) throws Exception { + PrivilegedExceptionAction pa; + pa = () -> getClass().getModule().getResourceAsStream(path); + InputStream is; + try { + is = AccessController.doPrivileged(pa); + } catch (PrivilegedActionException e) { + throw e.getException(); + } + return is; + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(keySet()); + } + + @Override + protected Set handleKeySet() { + if (keys == null) { + ResourceBundle info = getBreakIteratorInfo(); + Set k = info.keySet(); + k.removeAll(NON_DATA_KEYS); + synchronized (this) { + if (keys == null) { + keys = k; + } + } + } + return keys; + } +} diff --git a/jdk/src/java.base/share/classes/sun/util/resources/LocaleData.java b/jdk/src/java.base/share/classes/sun/util/resources/LocaleData.java index 5510b6cc250..497742667e7 100644 --- a/jdk/src/java.base/share/classes/sun/util/resources/LocaleData.java +++ b/jdk/src/java.base/share/classes/sun/util/resources/LocaleData.java @@ -122,6 +122,14 @@ public class LocaleData { return getBundle(type.getTextResourcesPackage() + ".BreakIteratorInfo", locale); } + /** + * Gets a break iterator resources resource bundle, using + * privileges to allow accessing a sun.* package. + */ + public ResourceBundle getBreakIteratorResources(Locale locale) { + return getBundle(type.getTextResourcesPackage() + ".BreakIteratorResources", locale); + } + /** * Gets a collation data resource bundle, using privileges * to allow accessing a sun.* package. diff --git a/jdk/src/jdk.localedata/share/classes/sun/text/resources/ext/BreakIteratorResources_th.java b/jdk/src/jdk.localedata/share/classes/sun/text/resources/ext/BreakIteratorResources_th.java new file mode 100644 index 00000000000..ea1012df911 --- /dev/null +++ b/jdk/src/jdk.localedata/share/classes/sun/text/resources/ext/BreakIteratorResources_th.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016, 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.text.resources.ext; + +import java.util.ResourceBundle; +import sun.util.resources.BreakIteratorResourceBundle; + +public class BreakIteratorResources_th extends BreakIteratorResourceBundle { + @Override + protected ResourceBundle getBreakIteratorInfo() { + return new BreakIteratorInfo_th(); + } +} diff --git a/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java b/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java index f32cb2a74ad..13848d91774 100644 --- a/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java +++ b/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2016, 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 @@ -89,7 +89,7 @@ public class BreakIteratorProviderTest extends ProviderTest { String[] jresResult = new String[4]; if (jreSupportsLocale) { for (int i = 0; i < 4; i++) { - jresResult[i] = "sun.util.locale.provider."+classNames[i]; + jresResult[i] = "sun.text." + classNames[i]; } } diff --git a/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.sh b/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.sh index f927fb16811..aeeb8e8fe03 100644 --- a/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.sh +++ b/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2007, 2016, 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 @@ -23,6 +23,6 @@ # # # @test -# @bug 4052440 8062588 +# @bug 4052440 8062588 8165804 # @summary BreakIteratorProvider tests # @run shell ExecTest.sh foo BreakIteratorProviderTest diff --git a/jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java b/jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java index a7cd181959f..c5963847d2a 100644 --- a/jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java +++ b/jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java @@ -40,7 +40,7 @@ import tests.Result; /* * @test - * @bug 8152143 8152704 8155649 + * @bug 8152143 8152704 8155649 8165804 * @summary IncludeLocalesPlugin tests * @author Naoto Sato * @library ../../lib @@ -236,6 +236,7 @@ public class IncludeLocalesPluginTest { "/jdk.localedata/sun/text/resources/ext/thai_dict", "/jdk.localedata/sun/text/resources/ext/WordBreakIteratorData_th", "/jdk.localedata/sun/text/resources/ext/BreakIteratorInfo_th.class", + "/jdk.localedata/sun/text/resources/ext/BreakIteratorResources_th.class", "/jdk.localedata/sun/text/resources/ext/FormatData_en_GB.class", "/jdk.localedata/sun/text/resources/ext/FormatData_ja.class", "/jdk.localedata/sun/text/resources/ext/FormatData_th.class", @@ -261,6 +262,7 @@ public class IncludeLocalesPluginTest { "/jdk.localedata/sun/text/resources/ext/thai_dict", "/jdk.localedata/sun/text/resources/ext/WordBreakIteratorData_th", "/jdk.localedata/sun/text/resources/ext/BreakIteratorInfo_th.class", + "/jdk.localedata/sun/text/resources/ext/BreakIteratorResources_th.class", "/jdk.localedata/sun/text/resources/ext/FormatData_th.class"), List.of( "/jdk.localedata/sun/text/resources/ext/FormatData_en_GB.class",