📄 numericshaper.java
字号:
'\u115a', '\u115f', '\u11a3', '\u11a8', '\u11fa', '\u1200', '\u1207', '\u1208', '\u1247', '\u1248', '\u1249', '\u124a', '\u124e', '\u1250', '\u1257', '\u1258', '\u1259', '\u125a', '\u125e', '\u1260', '\u1287', '\u1288', '\u1289', '\u128a', '\u128e', '\u1290', '\u12af', '\u12b0', '\u12b1', '\u12b2', '\u12b6', '\u12b8', '\u12bf', '\u12c0', '\u12c1', '\u12c2', '\u12c6', '\u12c8', '\u12cf', '\u12d0', '\u12d7', '\u12d8', '\u12ef', '\u12f0', '\u130f', '\u1310', '\u1311', '\u1312', '\u1316', '\u1318', '\u131f', '\u1320', '\u1347', '\u1348', '\u135b', '\u1361', '\u137d', '\u13a0', '\u13f5', '\u1401', '\u1677', '\u1681', '\u169b', '\u16a0', '\u16f1', '\u1780', '\u17b7', '\u17be', '\u17c6', '\u17c7', '\u17c9', '\u17d4', '\u17db', '\u17dc', '\u17dd', '\u17e0', '\u17ea', '\u1810', '\u181a', '\u1820', '\u1878', '\u1880', '\u18a9', '\u1e00', '\u1e9c', '\u1ea0', '\u1efa', '\u1f00', '\u1f16', '\u1f18', '\u1f1e', '\u1f20', '\u1f46', '\u1f48', '\u1f4e', '\u1f50', '\u1f58', '\u1f59', '\u1f5a', '\u1f5b', '\u1f5c', '\u1f5d', '\u1f5e', '\u1f5f', '\u1f7e', '\u1f80', '\u1fb5', '\u1fb6', '\u1fbd', '\u1fbe', '\u1fbf', '\u1fc2', '\u1fc5', '\u1fc6', '\u1fcd', '\u1fd0', '\u1fd4', '\u1fd6', '\u1fdc', '\u1fe0', '\u1fed', '\u1ff2', '\u1ff5', '\u1ff6', '\u1ffd', '\u200e', '\u2010', '\u207f', '\u2080', '\u2102', '\u2103', '\u2107', '\u2108', '\u210a', '\u2114', '\u2115', '\u2116', '\u2119', '\u211e', '\u2124', '\u2125', '\u2126', '\u2127', '\u2128', '\u2129', '\u212a', '\u212e', '\u212f', '\u2132', '\u2133', '\u213a', '\u2160', '\u2184', '\u2336', '\u237b', '\u2395', '\u2396', '\u249c', '\u24ea', '\u3005', '\u3008', '\u3021', '\u302a', '\u3031', '\u3036', '\u3038', '\u303b', '\u3041', '\u3095', '\u309d', '\u309f', '\u30a1', '\u30fb', '\u30fc', '\u30ff', '\u3105', '\u312d', '\u3131', '\u318f', '\u3190', '\u31b8', '\u3200', '\u321d', '\u3220', '\u3244', '\u3260', '\u327c', '\u327f', '\u32b1', '\u32c0', '\u32cc', '\u32d0', '\u32ff', '\u3300', '\u3377', '\u337b', '\u33de', '\u33e0', '\u33ff', '\u3400', '\u4db6', '\u4e00', '\u9fa6', '\ua000', '\ua48d', '\uac00', '\ud7a4', '\uf900', '\ufa2e', '\ufb00', '\ufb07', '\ufb13', '\ufb18', '\ufb1d', '\ufb1e', '\ufb1f', '\ufb29', '\ufb2a', '\ufb37', '\ufb38', '\ufb3d', '\ufb3e', '\ufb3f', '\ufb40', '\ufb42', '\ufb43', '\ufb45', '\ufb46', '\ufbb2', '\ufbd3', '\ufd3e', '\ufd50', '\ufd90', '\ufd92', '\ufdc8', '\ufdf0', '\ufdfc', '\ufe70', '\ufe73', '\ufe74', '\ufe75', '\ufe76', '\ufefd', '\uff21', '\uff3b', '\uff41', '\uff5b', '\uff66', '\uffbf', '\uffc2', '\uffc8', '\uffca', '\uffd0', '\uffd2', '\uffd8', '\uffda', '\uffdd', '\uffff' // last entry is sentinel, actually never checked }; // use a binary search with a cache private static int stCache = 0; // warning, synchronize access to this as it modifies state private static boolean isStrongDirectional(char c) { if (c < strongTable[stCache]) { stCache = search(c, strongTable, 0, stCache); } else if (c >= strongTable[stCache + 1]) { stCache = search(c, strongTable, stCache + 1, strongTable.length - stCache - 1); } return (stCache & 0x1) == 1; } static private int getKeyFromMask(int mask) { int key = 0; while (key < NUM_KEYS && ((mask & (1<<key)) == 0)) { ++key; } if (key == NUM_KEYS || ((mask & ~(1<<key)) != 0)) { throw new IllegalArgumentException("invalid shaper: " + Integer.toHexString(mask)); } return key; } /** * Returns a shaper for the provided unicode range. All * Latin-1 (EUROPEAN) digits are converted * to the corresponding decimal unicode digits. * @param singleRange the specified Unicode range * @return a non-contextual numeric shaper * @throws IllegalArgumentException if the range is not a single range */ static public NumericShaper getShaper(int singleRange) { int key = getKeyFromMask(singleRange); return new NumericShaper(key, singleRange); } /** * Returns a contextual shaper for the provided unicode range(s). * Latin-1 (EUROPEAN) digits are converted to the decimal digits * corresponding to the range of the preceeding text, if the * range is one of the provided ranges. Multiple ranges are * represented by or-ing the values together, such as, * <code>NumericShaper.ARABIC | NumericShaper.THAI</code>. The * shaper assumes EUROPEAN as the starting context, that is, if * EUROPEAN digits are encountered before any strong directional * text in the string, the context is presumed to be EUROPEAN, and * so the digits will not shape. * @param ranges the specified Unicode ranges * @return a shaper for the specified ranges */ static public NumericShaper getContextualShaper(int ranges) { ranges |= CONTEXTUAL_MASK; return new NumericShaper(EUROPEAN_KEY, ranges); } /** * Returns a contextual shaper for the provided unicode range(s). * Latin-1 (EUROPEAN) digits will be converted to the decimal digits * corresponding to the range of the preceeding text, if the * range is one of the provided ranges. Multiple ranges are * represented by or-ing the values together, for example, * <code>NumericShaper.ARABIC | NumericShaper.THAI</code>. The * shaper uses defaultContext as the starting context. * @param ranges the specified Unicode ranges * @param defaultContext the starting context, such as * <code>NumericShaper.EUROPEAN</code> * @return a shaper for the specified Unicode ranges. */ static public NumericShaper getContextualShaper(int ranges, int defaultContext) { int key = getKeyFromMask(defaultContext); ranges |= CONTEXTUAL_MASK; return new NumericShaper(key, ranges); } /** * Private constructor. */ private NumericShaper(int key, int mask) { this.key = key; this.mask = mask; } /** * Converts the digits in the text that occur between start and * start + count. * @param text an array of characters to convert * @param start the index into <code>text</code> to start * converting * @param count the number of characters in <code>text</code> * to convert */ public void shape(char[] text, int start, int count) { if (isContextual()) { shapeContextually(text, start, count, key); } else { shapeNonContextually(text, start, count); } } /** * Converts the digits in the text that occur between start and * start + count, using the provided context. * Context is ignored if the shaper is not a contextual shaper. * @param text an array of characters * @param start the index into <code>text</code> to start * converting * @param count the number of characters in <code>text</code> * to convert * @param context the context to which to convert the * characters, such as <code>NumericShaper.EUROPEAN</code> */ public void shape(char[] text, int start, int count, int context) { if (isContextual()) { int ctxKey = getKeyFromMask(context); shapeContextually(text, start, count, ctxKey); } else { shapeNonContextually(text, start, count); } } /** * Returns a <code>boolean</code> indicating whether or not * this shaper shapes contextually. * @return <code>true</code> if this shaper is contextual; * <code>false</code> otherwise. */ public boolean isContextual() { return (mask & CONTEXTUAL_MASK) != 0; } /** * Returns an <code>int</code> that ORs together the values for * all the ranges that will be shaped. * <p> * For example, to check if a shaper shapes to Arabic, you would use the * following: * <blockquote> * <code>if ((shaper.getRanges() & shaper.ARABIC) != 0) { ... </code> * </blockquote> * @return the values for all the ranges to be shaped. */ public int getRanges() { return mask & ~CONTEXTUAL_MASK; } /** * Perform non-contextual shaping. */ private void shapeNonContextually(char[] text, int start, int count) { int base = bases[key]; char minDigit = key == TAMIL_KEY ? '\u0031' : '\u0030'; // Tamil doesn't use decimal zero for (int i = start, e = start + count; i < e; ++i) { char c = text[i]; if (c >= minDigit && c <= '\u0039') { text[i] = (char)(c + base); } } } /** * Perform contextual shaping. * Synchronized to protect caches used in getContextKey and isStrongDirectional. */ private synchronized void shapeContextually(char[] text, int start, int count, int ctxKey) { // if we don't support this context, then don't shape if ((mask & (1<<ctxKey)) == 0) { ctxKey = EUROPEAN_KEY; } int lastkey = ctxKey; int base = bases[ctxKey]; char minDigit = ctxKey == TAMIL_KEY ? '\u0031' : '\u0030'; // Tamil doesn't use decimal zero for (int i = start, e = start + count; i < e; ++i) { char c = text[i]; if (c >= minDigit && c <= '\u0039') { text[i] = (char)(c + base); } if (isStrongDirectional(c)) { int newkey = getContextKey(c); if (newkey != lastkey) { lastkey = newkey; ctxKey = newkey; if (((mask & EASTERN_ARABIC) != 0) && (ctxKey == ARABIC_KEY || ctxKey == EASTERN_ARABIC_KEY)) { ctxKey = EASTERN_ARABIC_KEY; } else if ((mask & (1<<ctxKey)) == 0) { ctxKey = EUROPEAN_KEY; } base = bases[ctxKey]; minDigit = ctxKey == TAMIL_KEY ? '\u0031' : '\u0030'; // Tamil doesn't use decimal zero } } } } /** * Returns a hash code for this shaper. * @return this shaper's hash code. * @see java.lang.Object#hashCode */ public int hashCode() { return mask; } /** * Returns true if the specified object is an instance of * <code>NumericShaper</code> and shapes identically to this one. * @param o the specified object to compare to this * <code>NumericShaper</code> * @return <code>true</code> if <code>o</code> is an instance * of <code>NumericShaper</code> and shapes in the same way; * <code>false</code> otherwise. * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object o) { if (o != null) { try { NumericShaper rhs = (NumericShaper)o; return rhs.mask == mask && rhs.key == key; } catch (ClassCastException e) { } } return false; } /** * Returns a <code>String</code> that describes this shaper. This method * is used for debugging purposes only. * @return a <code>String</code> describing this shaper. */ public String toString() { StringBuffer buf = new StringBuffer(super.toString()); buf.append("[contextual:" + isContextual()); if (isContextual()) { buf.append(", context:" + keyNames[key]); } buf.append(", range(s): "); boolean first = true; for (int i = 0; i < NUM_KEYS; ++i) { if ((mask & (1 << i)) != 0) { if (first) { first = false; } else { buf.append(", "); } buf.append(keyNames[i]); } } buf.append(']'); return buf.toString(); } /** * Returns the index of the high bit in value (assuming le, actually * power of 2 >= value). value must be positive. */ private static int getHighBit(int value) { if (value <= 0) { return -32; } int bit = 0; if (value >= 1 << 16) { value >>= 16; bit += 16; } if (value >= 1 << 8) { value >>= 8; bit += 8; } if (value >= 1 << 4) { value >>= 4; bit += 4; } if (value >= 1 << 2) { value >>= 2; bit += 2; } if (value >= 1 << 1) { value >>= 1; bit += 1; } return bit; } /** * fast binary search over subrange of array. */ private static int search(char value, char[] array, int start, int length) { int power = 1 << getHighBit(length); int extra = length - power; int probe = power; int index = start; if (value >= array[index + extra]) { index += extra; } while (probe > 1) { probe >>= 1; if (value >= array[index + probe]) { index += probe; } } return index; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -