/* * Copyright (c) 2000, 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.awt.font; import java.io.IOException;import java.io.ObjectOutputStream;import java.util.Arrays;import java.util.Comparator;import java.util.EnumSet;import java.util.Set; /** * The <code>NumericShaper</code> class is used to convert Latin-1 (European) * digits to other Unicode decimal digits. Users of this class will * primarily be people who wish to present data using * national digit shapes, but find it more convenient to represent the * data internally using Latin-1 (European) digits. This does not * interpret the deprecated numeric shape selector character (U+206E). * <p> * Instances of <code>NumericShaper</code> are typically applied * as attributes to text with the * {@link TextAttribute#NUMERIC_SHAPING NUMERIC_SHAPING} attribute * of the <code>TextAttribute</code> class. * For example, this code snippet causes a <code>TextLayout</code> to * shape European digits to Arabic in an Arabic context:<br> * <blockquote><pre> * Map map = new HashMap(); * map.put(TextAttribute.NUMERIC_SHAPING, * NumericShaper.getContextualShaper(NumericShaper.ARABIC)); * FontRenderContext frc = ...; * TextLayout layout = new TextLayout(text, map, frc); * layout.draw(g2d, x, y); * </pre></blockquote> * <br> * It is also possible to perform numeric shaping explicitly using instances * of <code>NumericShaper</code>, as this code snippet demonstrates:<br> * <blockquote><pre> * char[] text = ...; * // shape all EUROPEAN digits (except zero) to ARABIC digits * NumericShaper shaper = NumericShaper.getShaper(NumericShaper.ARABIC); * shaper.shape(text, start, count); * * // shape European digits to ARABIC digits if preceding text is Arabic, or * // shape European digits to TAMIL digits if preceding text is Tamil, or * // leave European digits alone if there is no preceding text, or * // preceding text is neither Arabic nor Tamil * NumericShaper shaper = * NumericShaper.getContextualShaper(NumericShaper.ARABIC | * NumericShaper.TAMIL, * NumericShaper.EUROPEAN); * shaper.shape(text, start, count); * </pre></blockquote> * * <p><b>Bit mask- and enum-based Unicode ranges</b></p> * * <p>This class supports two different programming interfaces to * represent Unicode ranges for script-specific digits: bit * mask-based ones, such as {@link #ARABIC NumericShaper.ARABIC}, and * enum-based ones, such as {@link NumericShaper.Range#ARABIC}. * Multiple ranges can be specified by ORing bit mask-based constants, * such as: * <blockquote><pre> * NumericShaper.ARABIC | NumericShaper.TAMIL * </pre></blockquote> * or creating a {@code Set} with the {@link NumericShaper.Range} * constants, such as: * <blockquote><pre> * EnumSet.of(NumericShaper.Scirpt.ARABIC, NumericShaper.Range.TAMIL) * </pre></blockquote> * The enum-based ranges are a super set of the bit mask-based ones. * * <p>If the two interfaces are mixed (including serialization), * Unicode range values are mapped to their counterparts where such * mapping is possible, such as {@code NumericShaper.Range.ARABIC} * from/to {@code NumericShaper.ARABIC}. If any unmappable range * values are specified, such as {@code NumericShaper.Range.BALINESE}, * those ranges are ignored. * * <p><b>Decimal Digits Precedence</b></p> * * <p>A Unicode range may have more than one set of decimal digits. If * multiple decimal digits sets are specified for the same Unicode * range, one of the sets will take precedence as follows. * * <table border=1 cellspacing=3 cellpadding=0 summary="NumericShaper constants precedence."> * <tr> * <th class="TableHeadingColor">Unicode Range</th> * <th class="TableHeadingColor"><code>NumericShaper</code> Constants</th> * <th class="TableHeadingColor">Precedence</th> * </tr> * <tr> * <td rowspan="2">Arabic</td> * <td>{@link NumericShaper#ARABIC NumericShaper.ARABIC}<br> * {@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC}</td> * <td>{@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC}</td> * </tr> * <tr> * <td>{@link NumericShaper.Range#ARABIC}<br> * {@link NumericShaper.Range#EASTERN_ARABIC}</td> * <td>{@link NumericShaper.Range#EASTERN_ARABIC}</td> * </tr> * <tr> * <td>Tai Tham</td> * <td>{@link NumericShaper.Range#TAI_THAM_HORA}<br> * {@link NumericShaper.Range#TAI_THAM_THAM}</td> * <td>{@link NumericShaper.Range#TAI_THAM_THAM}</td> * </tr> * </table> * * @since 1.4 */ public final class NumericShaper implements java.io.Serializable { /** * A {@code NumericShaper.Range} represents a Unicode range of a * script having its own decimal digits. For example, the {@link * NumericShaper.Range#THAI} range has the Thai digits, THAI DIGIT * ZERO (U+0E50) to THAI DIGIT NINE (U+0E59). * * <p>The <code>Range</code> enum replaces the traditional bit * mask-based values (e.g., {@link NumericShaper#ARABIC}), and * supports more Unicode ranges than the bit mask-based ones. For * example, the following code using the bit mask: * <blockquote><pre> * NumericShaper.getContextualShaper(NumericShaper.ARABIC | * NumericShaper.TAMIL, * NumericShaper.EUROPEAN); * </pre></blockquote> * can be written using this enum as: * <blockquote><pre> * NumericShaper.getContextualShaper(EnumSet.of( * NumericShaper.Range.ARABIC, * NumericShaper.Range.TAMIL), * NumericShaper.Range.EUROPEAN); * </pre></blockquote> * * @since 1.7 */ public static enum Range { // The order of EUROPEAN to MOGOLIAN must be consistent // with the bitmask-based constants. /** * The Latin (European) range with the Latin (ASCII) digits. */ EUROPEAN ('\u0030', '\u0000', '\u0300'), /** * The Arabic range with the Arabic-Indic digits. */ ARABIC ('\u0660', '\u0600', '\u0780'), /** * The Arabic range with the Eastern Arabic-Indic digits. */ EASTERN_ARABIC ('\u06f0', '\u0600', '\u0780'), /** * The Devanagari range with the Devanagari digits. */ DEVANAGARI ('\u0966', '\u0900', '\u0980'), /** * The Bengali range with the Bengali digits. */ BENGALI ('\u09e6', '\u0980', '\u0a00'), /** * The Gurmukhi range with the Gurmukhi digits. */ GURMUKHI ('\u0a66', '\u0a00', '\u0a80'), /** * The Gujarati range with the Gujarati digits. */ GUJARATI ('\u0ae6', '\u0b00', '\u0b80'), /** * The Oriya range with the Oriya digits. */ ORIYA ('\u0b66', '\u0b00', '\u0b80'), /** * The Tamil range with the Tamil digits. */ TAMIL ('\u0be6', '\u0b80', '\u0c00'), /** * The Telugu range with the Telugu digits. */ TELUGU ('\u0c66', '\u0c00', '\u0c80'), /** * The Kannada range with the Kannada digits. */ KANNADA ('\u0ce6', '\u0c80', '\u0d00'), /** * The Malayalam range with the Malayalam digits. */ MALAYALAM ('\u0d66', '\u0d00', '\u0d80'), /** * The Thai range with the Thai digits. */ THAI ('\u0e50', '\u0e00', '\u0e80'), /** * The Lao range with the Lao digits. */ LAO ('\u0ed0', '\u0e80', '\u0f00'), /** * The Tibetan range with the Tibetan digits. */ TIBETAN ('\u0f20', '\u0f00', '\u1000'), /** * The Myanmar range with the Myanmar digits. */ MYANMAR ('\u1040', '\u1000', '\u1080'), /** * The Ethiopic range with the Ethiopic digits. Ethiopic * does not have a decimal digit 0 so Latin (European) 0 is * used. */ ETHIOPIC ('\u1369', '\u1200', '\u1380') { @Override char getNumericBase() { return 1; } }, /** * The Khmer range with the Khmer digits. */ KHMER ('\u17e0', '\u1780', '\u1800'), /** * The Mongolian range with the Mongolian digits. */ MONGOLIAN ('\u1810', '\u1800', '\u1900'), // The order of EUROPEAN to MOGOLIAN must be consistent // with the bitmask-based constants. /** * The N'Ko range with the N'Ko digits. */ NKO ('\u07c0', '\u07c0', '\u0800'), /** * The Myanmar range with the Myanmar Shan digits. */ MYANMAR_SHAN ('\u1090', '\u1000', '\u10a0'), /** * The Limbu range with the Limbu digits. */ LIMBU ('\u1946', '\u1900', '\u1950'), /** * The New Tai Lue range with the New Tai Lue digits. */ NEW_TAI_LUE ('\u19d0', '\u1980', '\u19e0'), /** * The Balinese range with the Balinese digits. */ BALINESE ('\u1b50', '\u1b00', '\u1b80'), /** * The Sundanese range with the Sundanese digits. */ SUNDANESE ('\u1bb0', '\u1b80', '\u1bc0'), /** * The Lepcha range with the Lepcha digits. */ LEPCHA ('\u1c40', '\u1c00', '\u1c50'), /** * The Ol Chiki range with the Ol Chiki digits. */ OL_CHIKI ('\u1c50', '\u1c50', '\u1c80'), /** * The Vai range with the Vai digits. */ VAI ('\ua620', '\ua500', '\ua640'), /** * The Saurashtra range with the Saurashtra digits. */ SAURASHTRA ('\ua8d0', '\ua880', '\ua8e0'), /** * The Kayah Li range with the Kayah Li digits. */ KAYAH_LI ('\ua900', '\ua900', '\ua930'), /** * The Cham range with the Cham digits. */ CHAM ('\uaa50', '\uaa00', '\uaa60'), /** * The Tai Tham Hora range with the Tai Tham Hora digits. */ TAI_THAM_HORA ('\u1a80', '\u1a20', '\u1ab0'), /** * The Tai Tham Tham range with the Tai Tham Tham digits. */ TAI_THAM_THAM ('\u1a90', '\u1a20', '\u1ab0'), /** * The Javanese range with the Javanese digits. */ JAVANESE ('\ua9d0', '\ua980', '\ua9e0'), /** * The Meetei Mayek range with the Meetei Mayek digits. */ MEETEI_MAYEK ('\uabf0', '\uabc0', '\uac00'); private static int toRangeIndex(Range script) { int index = script.ordinal(); return index < NUM_KEYS ? index : -1; } private static Range indexToRange(int index) { return index < NUM_KEYS ? Range.values()[index] : null; } private static int toRangeMask(Set<Range> ranges) { int m = 0; for (Range range : ranges) { int index = range.ordinal(); if (index < NUM_KEYS) { m |= 1 << index; } } return m; } private static Set<Range> maskToRangeSet(int mask) { Set<Range> set = EnumSet.noneOf(Range.class); Range[] a = Range.values(); for (int i = 0; i < NUM_KEYS; i++) { if ((mask & (1 << i)) != 0) { set.add(a[i]); } } return set; } // base character of range digits private final int base; // Unicode range private final int start, // inclusive end; // exclusive private Range(int base, int start, int end) { this.base = base - ('0' + getNumericBase()); this.start = start; this.end = end; } private int getDigitBase() { return base; } char getNumericBase() { return 0; } private boolean inRange(int c) { return start <= c && c < end; } } /** index of context for contextual shaping - values range from 0 to 18 */ private int key; /** flag indicating whether to shape contextually (high bit) and which * digit ranges to shape (bits 0-18) */ private int mask; /** * The context {@code Range} for contextual shaping or the {@code * Range} for non-contextual shaping. {@code null} for the bit * mask-based API. * * @since 1.7 */ private Range shapingRange; /** * {@code Set<Range>} indicating which Unicode ranges to * shape. {@code null} for the bit mask-based API. */ private transient Set<Range> rangeSet; /** * rangeSet.toArray() value. Sorted by Range.base when the number * of elements is greater then BSEARCH_THRESHOLD. */ private transient Range[] rangeArray; /** * If more than BSEARCH_THRESHOLD ranges are specified, binary search is used. */ private static final int BSEARCH_THRESHOLD = 3; private static final long serialVersionUID = -8022764705923730308L; /** Identifies the Latin-1 (European) and extended range, and * Latin-1 (European) decimal base. */ public static final int EUROPEAN = 1<<0; /** Identifies the ARABIC range and decimal base. */ public static final int ARABIC = 1<<1; /** Identifies the ARABIC range and ARABIC_EXTENDED decimal base. */ public static final int EASTERN_ARABIC = 1<<2; /** Identifies the DEVANAGARI range and decimal base. */ public static final int DEVANAGARI = 1<<3; /** Identifies the BENGALI range and decimal base. */ public static final int BENGALI = 1<<4; /** Identifies the GURMUKHI range and decimal base. */ public static final int GURMUKHI = 1<<5; /** Identifies the GUJARATI range and decimal base. */ public static final int GUJARATI = 1<<6; /** Identifies the ORIYA range and decimal base. */ public static final int ORIYA = 1<<7; /** Identifies the TAMIL range and decimal base. */ // TAMIL DIGIT ZERO was added in Unicode 4.1 public static final int TAMIL = 1<<8; /** Identifies the TELUGU range and decimal base. */ public static final int TELUGU = 1<<9; /** Identifies the KANNADA range and decimal base. */ public static final int KANNADA = 1<<10; /** Identifies the MALAYALAM range and decimal base. */ public static final int MALAYALAM = 1<<11; /** Identifies the THAI range and decimal base. */ public static final int THAI = 1<<12; /** Identifies the LAO range and decimal base. */ public static final int LAO = 1<<13; /** Identifies the TIBETAN range and decimal base. */ public static final int TIBETAN = 1<<14; /** Identifies the MYANMAR range and decimal base. */ public static final int MYANMAR = 1<<15; /** Identifies the ETHIOPIC range and decimal base. */ public static final int ETHIOPIC = 1<<16; /** Identifies the KHMER range and decimal base. */ public static final int KHMER = 1<<17; /** Identifies the MONGOLIAN range and decimal base. */ public static final int MONGOLIAN = 1<<18; /** Identifies all ranges, for full contextual shaping. * * <p>This constant specifies all of the bit mask-based * ranges. Use {@code EmunSet.allOf(NumericShaper.Range.class)} to * specify all of the enum-based ranges. */ public static final int ALL_RANGES = 0x0007ffff; private static final int EUROPEAN_KEY = 0; private static final int ARABIC_KEY = 1; private static final int EASTERN_ARABIC_KEY = 2; private static final int DEVANAGARI_KEY = 3; private static final int BENGALI_KEY = 4; private static final int GURMUKHI_KEY = 5; private static final int GUJARATI_KEY = 6; private static final int ORIYA_KEY = 7; private static final int TAMIL_KEY = 8; private static final int TELUGU_KEY = 9; private static final int KANNADA_KEY = 10; private static final int MALAYALAM_KEY = 11; private static final int THAI_KEY = 12; private static final int LAO_KEY = 13; private static final int TIBETAN_KEY = 14; private static final int MYANMAR_KEY = 15; private static final int ETHIOPIC_KEY = 16; private static final int KHMER_KEY = 17; private static final int MONGOLIAN_KEY = 18; private static final int NUM_KEYS = MONGOLIAN_KEY + 1; // fixed private static final int CONTEXTUAL_MASK = 1<<31; private static final char[] bases = { '\u0030' - '\u0030', // EUROPEAN '\u0660' - '\u0030', // ARABIC-INDICLines 1–500 of 1507. Load more
/* * Copyright (c) 2000, 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.awt.font; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.Comparator; import java.util.EnumSet; import java.util.Set; /** * The <code>NumericShaper</code> class is used to convert Latin-1 (European) * digits to other Unicode decimal digits. Users of this class will * primarily be people who wish to present data using * national digit shapes, but find it more convenient to represent the * data internally using Latin-1 (European) digits. This does not * interpret the deprecated numeric shape selector character (U+206E). * <p> * Instances of <code>NumericShaper</code> are typically applied * as attributes to text with the * {@link TextAttribute#NUMERIC_SHAPING NUMERIC_SHAPING} attribute * of the <code>TextAttribute</code> class. * For example, this code snippet causes a <code>TextLayout</code> to * shape European digits to Arabic in an Arabic context:<br> * <blockquote><pre> * Map map = new HashMap(); * map.put(TextAttribute.NUMERIC_SHAPING, * NumericShaper.getContextualShaper(NumericShaper.ARABIC)); * FontRenderContext frc = ...; * TextLayout layout = new TextLayout(text, map, frc); * layout.draw(g2d, x, y); * </pre></blockquote> * <br> * It is also possible to perform numeric shaping explicitly using instances * of <code>NumericShaper</code>, as this code snippet demonstrates:<br> * <blockquote><pre> * char[] text = ...; * // shape all EUROPEAN digits (except zero) to ARABIC digits * NumericShaper shaper = NumericShaper.getShaper(NumericShaper.ARABIC); * shaper.shape(text, start, count); * * // shape European digits to ARABIC digits if preceding text is Arabic, or * // shape European digits to TAMIL digits if preceding text is Tamil, or * // leave European digits alone if there is no preceding text, or * // preceding text is neither Arabic nor Tamil * NumericShaper shaper = * NumericShaper.getContextualShaper(NumericShaper.ARABIC | * NumericShaper.TAMIL, * NumericShaper.EUROPEAN); * shaper.shape(text, start, count); * </pre></blockquote> * * <p><b>Bit mask- and enum-based Unicode ranges</b></p> * * <p>This class supports two different programming interfaces to * represent Unicode ranges for script-specific digits: bit * mask-based ones, such as {@link #ARABIC NumericShaper.ARABIC}, and * enum-based ones, such as {@link NumericShaper.Range#ARABIC}. * Multiple ranges can be specified by ORing bit mask-based constants, * such as: * <blockquote><pre> * NumericShaper.ARABIC | NumericShaper.TAMIL * </pre></blockquote> * or creating a {@code Set} with the {@link NumericShaper.Range} * constants, such as: * <blockquote><pre> * EnumSet.of(NumericShaper.Scirpt.ARABIC, NumericShaper.Range.TAMIL) * </pre></blockquote> * The enum-based ranges are a super set of the bit mask-based ones. * * <p>If the two interfaces are mixed (including serialization), * Unicode range values are mapped to their counterparts where such * mapping is possible, such as {@code NumericShaper.Range.ARABIC} * from/to {@code NumericShaper.ARABIC}. If any unmappable range * values are specified, such as {@code NumericShaper.Range.BALINESE}, * those ranges are ignored. * * <p><b>Decimal Digits Precedence</b></p> * * <p>A Unicode range may have more than one set of decimal digits. If * multiple decimal digits sets are specified for the same Unicode * range, one of the sets will take precedence as follows. * * <table border=1 cellspacing=3 cellpadding=0 summary="NumericShaper constants precedence."> * <tr> * <th class="TableHeadingColor">Unicode Range</th> * <th class="TableHeadingColor"><code>NumericShaper</code> Constants</th> * <th class="TableHeadingColor">Precedence</th> * </tr> * <tr> * <td rowspan="2">Arabic</td> * <td>{@link NumericShaper#ARABIC NumericShaper.ARABIC}<br> * {@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC}</td> * <td>{@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC}</td> * </tr> * <tr> * <td>{@link NumericShaper.Range#ARABIC}<br> * {@link NumericShaper.Range#EASTERN_ARABIC}</td> * <td>{@link NumericShaper.Range#EASTERN_ARABIC}</td> * </tr> * <tr> * <td>Tai Tham</td> * <td>{@link NumericShaper.Range#TAI_THAM_HORA}<br> * {@link NumericShaper.Range#TAI_THAM_THAM}</td> * <td>{@link NumericShaper.Range#TAI_THAM_THAM}</td> * </tr> * </table> * * @since 1.4 */ public final class NumericShaper implements java.io.Serializable { /** * A {@code NumericShaper.Range} represents a Unicode range of a * script having its own decimal digits. For example, the {@link * NumericShaper.Range#THAI} range has the Thai digits, THAI DIGIT * ZERO (U+0E50) to THAI DIGIT NINE (U+0E59). * * <p>The <code>Range</code> enum replaces the traditional bit * mask-based values (e.g., {@link NumericShaper#ARABIC}), and * supports more Unicode ranges than the bit mask-based ones. For * example, the following code using the bit mask: * <blockquote><pre> * NumericShaper.getContextualShaper(NumericShaper.ARABIC | * NumericShaper.TAMIL, * NumericShaper.EUROPEAN); * </pre></blockquote> * can be written using this enum as: * <blockquote><pre> * NumericShaper.getContextualShaper(EnumSet.of( * NumericShaper.Range.ARABIC, * NumericShaper.Range.TAMIL), * NumericShaper.Range.EUROPEAN); * </pre></blockquote> * * @since 1.7 */ public static enum Range { // The order of EUROPEAN to MOGOLIAN must be consistent // with the bitmask-based constants. /** * The Latin (European) range with the Latin (ASCII) digits. */ EUROPEAN ('\u0030', '\u0000', '\u0300'), /** * The Arabic range with the Arabic-Indic digits. */ ARABIC ('\u0660', '\u0600', '\u0780'), /** * The Arabic range with the Eastern Arabic-Indic digits. */ EASTERN_ARABIC ('\u06f0', '\u0600', '\u0780'), /** * The Devanagari range with the Devanagari digits. */ DEVANAGARI ('\u0966', '\u0900', '\u0980'), /** * The Bengali range with the Bengali digits. */ BENGALI ('\u09e6', '\u0980', '\u0a00'), /** * The Gurmukhi range with the Gurmukhi digits. */ GURMUKHI ('\u0a66', '\u0a00', '\u0a80'), /** * The Gujarati range with the Gujarati digits. */ GUJARATI ('\u0ae6', '\u0b00', '\u0b80'), /** * The Oriya range with the Oriya digits. */ ORIYA ('\u0b66', '\u0b00', '\u0b80'), /** * The Tamil range with the Tamil digits. */ TAMIL ('\u0be6', '\u0b80', '\u0c00'), /** * The Telugu range with the Telugu digits. */ TELUGU ('\u0c66', '\u0c00', '\u0c80'), /** * The Kannada range with the Kannada digits. */ KANNADA ('\u0ce6', '\u0c80', '\u0d00'), /** * The Malayalam range with the Malayalam digits. */ MALAYALAM ('\u0d66', '\u0d00', '\u0d80'), /** * The Thai range with the Thai digits. */ THAI ('\u0e50', '\u0e00', '\u0e80'), /** * The Lao range with the Lao digits. */ LAO ('\u0ed0', '\u0e80', '\u0f00'), /** * The Tibetan range with the Tibetan digits. */ TIBETAN ('\u0f20', '\u0f00', '\u1000'), /** * The Myanmar range with the Myanmar digits. */ MYANMAR ('\u1040', '\u1000', '\u1080'), /** * The Ethiopic range with the Ethiopic digits. Ethiopic * does not have a decimal digit 0 so Latin (European) 0 is * used. */ ETHIOPIC ('\u1369', '\u1200', '\u1380') { @Override char getNumericBase() { return 1; } }, /** * The Khmer range with the Khmer digits. */ KHMER ('\u17e0', '\u1780', '\u1800'), /** * The Mongolian range with the Mongolian digits. */ MONGOLIAN ('\u1810', '\u1800', '\u1900'), // The order of EUROPEAN to MOGOLIAN must be consistent // with the bitmask-based constants. /** * The N'Ko range with the N'Ko digits. */ NKO ('\u07c0', '\u07c0', '\u0800'), /** * The Myanmar range with the Myanmar Shan digits. */ MYANMAR_SHAN ('\u1090', '\u1000', '\u10a0'), /** * The Limbu range with the Limbu digits. */ LIMBU ('\u1946', '\u1900', '\u1950'), /** * The New Tai Lue range with the New Tai Lue digits. */ NEW_TAI_LUE ('\u19d0', '\u1980', '\u19e0'), /** * The Balinese range with the Balinese digits. */ BALINESE ('\u1b50', '\u1b00', '\u1b80'), /** * The Sundanese range with the Sundanese digits. */ SUNDANESE ('\u1bb0', '\u1b80', '\u1bc0'), /** * The Lepcha range with the Lepcha digits. */ LEPCHA ('\u1c40', '\u1c00', '\u1c50'), /** * The Ol Chiki range with the Ol Chiki digits. */ OL_CHIKI ('\u1c50', '\u1c50', '\u1c80'), /** * The Vai range with the Vai digits. */ VAI ('\ua620', '\ua500', '\ua640'), /** * The Saurashtra range with the Saurashtra digits. */ SAURASHTRA ('\ua8d0', '\ua880', '\ua8e0'), /** * The Kayah Li range with the Kayah Li digits. */ KAYAH_LI ('\ua900', '\ua900', '\ua930'), /** * The Cham range with the Cham digits. */ CHAM ('\uaa50', '\uaa00', '\uaa60'), /** * The Tai Tham Hora range with the Tai Tham Hora digits. */ TAI_THAM_HORA ('\u1a80', '\u1a20', '\u1ab0'), /** * The Tai Tham Tham range with the Tai Tham Tham digits. */ TAI_THAM_THAM ('\u1a90', '\u1a20', '\u1ab0'), /** * The Javanese range with the Javanese digits. */ JAVANESE ('\ua9d0', '\ua980', '\ua9e0'), /** * The Meetei Mayek range with the Meetei Mayek digits. */ MEETEI_MAYEK ('\uabf0', '\uabc0', '\uac00'); private static int toRangeIndex(Range script) { int index = script.ordinal(); return index < NUM_KEYS ? index : -1; } private static Range indexToRange(int index) { return index < NUM_KEYS ? Range.values()[index] : null; } private static int toRangeMask(Set<Range> ranges) { int m = 0; for (Range range : ranges) { int index = range.ordinal(); if (index < NUM_KEYS) { m |= 1 << index; } } return m; } private static Set<Range> maskToRangeSet(int mask) { Set<Range> set = EnumSet.noneOf(Range.class); Range[] a = Range.values(); for (int i = 0; i < NUM_KEYS; i++) { if ((mask & (1 << i)) != 0) { set.add(a[i]); } } return set; } // base character of range digits private final int base; // Unicode range private final int start, // inclusive end; // exclusive private Range(int base, int start, int end) { this.base = base - ('0' + getNumericBase()); this.start = start; this.end = end; } private int getDigitBase() { return base; } char getNumericBase() { return 0; } private boolean inRange(int c) { return start <= c && c < end; } } /** index of context for contextual shaping - values range from 0 to 18 */ private int key; /** flag indicating whether to shape contextually (high bit) and which * digit ranges to shape (bits 0-18) */ private int mask; /** * The context {@code Range} for contextual shaping or the {@code * Range} for non-contextual shaping. {@code null} for the bit * mask-based API. * * @since 1.7 */ private Range shapingRange; /** * {@code Set<Range>} indicating which Unicode ranges to * shape. {@code null} for the bit mask-based API. */ private transient Set<Range> rangeSet; /** * rangeSet.toArray() value. Sorted by Range.base when the number * of elements is greater then BSEARCH_THRESHOLD. */ private transient Range[] rangeArray; /** * If more than BSEARCH_THRESHOLD ranges are specified, binary search is used. */ private static final int BSEARCH_THRESHOLD = 3; private static final long serialVersionUID = -8022764705923730308L; /** Identifies the Latin-1 (European) and extended range, and * Latin-1 (European) decimal base. */ public static final int EUROPEAN = 1<<0; /** Identifies the ARABIC range and decimal base. */ public static final int ARABIC = 1<<1; /** Identifies the ARABIC range and ARABIC_EXTENDED decimal base. */ public static final int EASTERN_ARABIC = 1<<2; /** Identifies the DEVANAGARI range and decimal base. */ public static final int DEVANAGARI = 1<<3; /** Identifies the BENGALI range and decimal base. */ public static final int BENGALI = 1<<4; /** Identifies the GURMUKHI range and decimal base. */ public static final int GURMUKHI = 1<<5; /** Identifies the GUJARATI range and decimal base. */ public static final int GUJARATI = 1<<6; /** Identifies the ORIYA range and decimal base. */ public static final int ORIYA = 1<<7; /** Identifies the TAMIL range and decimal base. */ // TAMIL DIGIT ZERO was added in Unicode 4.1 public static final int TAMIL = 1<<8; /** Identifies the TELUGU range and decimal base. */ public static final int TELUGU = 1<<9; /** Identifies the KANNADA range and decimal base. */ public static final int KANNADA = 1<<10; /** Identifies the MALAYALAM range and decimal base. */ public static final int MALAYALAM = 1<<11; /** Identifies the THAI range and decimal base. */ public static final int THAI = 1<<12; /** Identifies the LAO range and decimal base. */ public static final int LAO = 1<<13; /** Identifies the TIBETAN range and decimal base. */ public static final int TIBETAN = 1<<14; /** Identifies the MYANMAR range and decimal base. */ public static final int MYANMAR = 1<<15; /** Identifies the ETHIOPIC range and decimal base. */ public static final int ETHIOPIC = 1<<16; /** Identifies the KHMER range and decimal base. */ public static final int KHMER = 1<<17; /** Identifies the MONGOLIAN range and decimal base. */ public static final int MONGOLIAN = 1<<18; /** Identifies all ranges, for full contextual shaping. * * <p>This constant specifies all of the bit mask-based * ranges. Use {@code EmunSet.allOf(NumericShaper.Range.class)} to * specify all of the enum-based ranges. */ public static final int ALL_RANGES = 0x0007ffff; private static final int EUROPEAN_KEY = 0; private static final int ARABIC_KEY = 1; private static final int EASTERN_ARABIC_KEY = 2; private static final int DEVANAGARI_KEY = 3; private static final int BENGALI_KEY = 4; private static final int GURMUKHI_KEY = 5; private static final int GUJARATI_KEY = 6; private static final int ORIYA_KEY = 7; private static final int TAMIL_KEY = 8; private static final int TELUGU_KEY = 9; private static final int KANNADA_KEY = 10; private static final int MALAYALAM_KEY = 11; private static final int THAI_KEY = 12; private static final int LAO_KEY = 13; private static final int TIBETAN_KEY = 14; private static final int MYANMAR_KEY = 15; private static final int ETHIOPIC_KEY = 16; private static final int KHMER_KEY = 17; private static final int MONGOLIAN_KEY = 18; private static final int NUM_KEYS = MONGOLIAN_KEY + 1; // fixed private static final int CONTEXTUAL_MASK = 1<<31; private static final char[] bases = { '\u0030' - '\u0030', // EUROPEAN '\u0660' - '\u0030', // ARABIC-INDIC