UBiDiProps.java
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
*
* Copyright (C) 2004-2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
* file name: UBiDiProps.java
* encoding: US-ASCII
* tab size: 8 (not used)
* indentation:4
*
* created on: 2005jan16
* created by: Markus W. Scherer
*
* Low-level Unicode bidi/shaping properties access.
* Java port of ubidi_props.h/.c.
*/
package com.ibm.icu.impl;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.lang.UProperty;
import com.ibm.icu.text.UnicodeSet;
import com.ibm.icu.util.ICUUncheckedIOException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Iterator;
public final class UBiDiProps {
// constructors etc. --------------------------------------------------- ***
// port of ubidi_openProps()
private UBiDiProps() throws IOException {
ByteBuffer bytes = ICUBinary.getData(DATA_FILE_NAME);
readData(bytes);
}
private void readData(ByteBuffer bytes) throws IOException {
// read the header
ICUBinary.readHeader(bytes, FMT, new IsAcceptable());
// read indexes[]
int i, count;
count = bytes.getInt();
if (count < IX_TOP) {
throw new IOException("indexes[0] too small in " + DATA_FILE_NAME);
}
indexes = new int[count];
indexes[0] = count;
for (i = 1; i < count; ++i) {
indexes[i] = bytes.getInt();
}
// read the trie
trie = Trie2_16.createFromSerialized(bytes);
int expectedTrieLength = indexes[IX_TRIE_SIZE];
int trieLength = trie.getSerializedLength();
if (trieLength > expectedTrieLength) {
throw new IOException(DATA_FILE_NAME + ": not enough bytes for the trie");
}
// skip padding after trie bytes
ICUBinary.skipBytes(bytes, expectedTrieLength - trieLength);
// read mirrors[]
count = indexes[IX_MIRROR_LENGTH];
if (count > 0) {
mirrors = ICUBinary.getInts(bytes, count, 0);
}
// read jgArray[]
count = indexes[IX_JG_LIMIT] - indexes[IX_JG_START];
jgArray = new byte[count];
bytes.get(jgArray);
// read jgArray2[]
count = indexes[IX_JG_LIMIT2] - indexes[IX_JG_START2];
jgArray2 = new byte[count];
bytes.get(jgArray2);
}
// implement ICUBinary.Authenticate
private static final class IsAcceptable implements ICUBinary.Authenticate {
@Override
public boolean isDataVersionAcceptable(byte version[]) {
return version[0] == 2;
}
}
// set of property starts for UnicodeSet ------------------------------- ***
public final void addPropertyStarts(UnicodeSet set) {
int i, length;
int c, start, limit;
byte prev, jg;
/* add the start code point of each same-value range of the trie */
Iterator<Trie2.Range> trieIterator = trie.iterator();
Trie2.Range range;
while (trieIterator.hasNext() && !(range = trieIterator.next()).leadSurrogate) {
set.add(range.startCodePoint);
}
/* add the code points from the bidi mirroring table */
length = indexes[IX_MIRROR_LENGTH];
for (i = 0; i < length; ++i) {
c = getMirrorCodePoint(mirrors[i]);
set.add(c, c + 1);
}
/* add the code points from the Joining_Group array where the value changes */
start = indexes[IX_JG_START];
limit = indexes[IX_JG_LIMIT];
byte[] jga = jgArray;
for (; ; ) {
length = limit - start;
prev = 0;
for (i = 0; i < length; ++i) {
jg = jga[i];
if (jg != prev) {
set.add(start);
prev = jg;
}
++start;
}
if (prev != 0) {
/* add the limit code point if the last value was not 0 (it is now start==limit) */
set.add(limit);
}
if (limit == indexes[IX_JG_LIMIT]) {
/* switch to the second Joining_Group range */
start = indexes[IX_JG_START2];
limit = indexes[IX_JG_LIMIT2];
jga = jgArray2;
} else {
break;
}
}
/* add code points with hardcoded properties, plus the ones following them */
/* (none right now) */
}
// property access functions ------------------------------------------- ***
public final int getMaxValue(int which) {
int max;
max = indexes[IX_MAX_VALUES];
switch (which) {
case UProperty.BIDI_CLASS:
return (max & CLASS_MASK);
case UProperty.JOINING_GROUP:
return (max & MAX_JG_MASK) >> MAX_JG_SHIFT;
case UProperty.JOINING_TYPE:
return (max & JT_MASK) >> JT_SHIFT;
case UProperty.BIDI_PAIRED_BRACKET_TYPE:
return (max & BPT_MASK) >> BPT_SHIFT;
default:
return -1; /* undefined */
}
}
public final int getClass(int c) {
return getClassFromProps(trie.get(c));
}
public final boolean isMirrored(int c) {
return getFlagFromProps(trie.get(c), IS_MIRRORED_SHIFT);
}
private final int getMirror(int c, int props) {
int delta = getMirrorDeltaFromProps(props);
if (delta != ESC_MIRROR_DELTA) {
return c + delta;
} else {
/* look for mirror code point in the mirrors[] table */
int m;
int i, length;
int c2;
length = indexes[IX_MIRROR_LENGTH];
/* linear search */
for (i = 0; i < length; ++i) {
m = mirrors[i];
c2 = getMirrorCodePoint(m);
if (c == c2) {
/* found c, return its mirror code point using the index in m */
return getMirrorCodePoint(mirrors[getMirrorIndex(m)]);
} else if (c < c2) {
break;
}
}
/* c not found, return it itself */
return c;
}
}
public final int getMirror(int c) {
int props = trie.get(c);
return getMirror(c, props);
}
public final boolean isBidiControl(int c) {
return getFlagFromProps(trie.get(c), BIDI_CONTROL_SHIFT);
}
public final boolean isJoinControl(int c) {
return getFlagFromProps(trie.get(c), JOIN_CONTROL_SHIFT);
}
public final int getJoiningType(int c) {
return (trie.get(c) & JT_MASK) >> JT_SHIFT;
}
public final int getJoiningGroup(int c) {
int start, limit;
start = indexes[IX_JG_START];
limit = indexes[IX_JG_LIMIT];
if (start <= c && c < limit) {
return jgArray[c - start] & 0xff;
}
start = indexes[IX_JG_START2];
limit = indexes[IX_JG_LIMIT2];
if (start <= c && c < limit) {
return jgArray2[c - start] & 0xff;
}
return UCharacter.JoiningGroup.NO_JOINING_GROUP;
}
public final int getPairedBracketType(int c) {
return (trie.get(c) & BPT_MASK) >> BPT_SHIFT;
}
public final int getPairedBracket(int c) {
int props = trie.get(c);
if ((props & BPT_MASK) == 0) {
return c;
} else {
return getMirror(c, props);
}
}
// data members -------------------------------------------------------- ***
private int indexes[];
private int mirrors[];
private byte jgArray[];
private byte jgArray2[];
private Trie2_16 trie;
// data format constants ----------------------------------------------- ***
private static final String DATA_NAME = "ubidi";
private static final String DATA_TYPE = "icu";
private static final String DATA_FILE_NAME = DATA_NAME + "." + DATA_TYPE;
/* format "BiDi" */
private static final int FMT = 0x42694469;
/* indexes into indexes[] */
// private static final int IX_INDEX_TOP=0;
// private static final int IX_LENGTH=1;
private static final int IX_TRIE_SIZE = 2;
private static final int IX_MIRROR_LENGTH = 3;
private static final int IX_JG_START = 4;
private static final int IX_JG_LIMIT = 5;
private static final int IX_JG_START2 = 6; /* new in format version 2.2, ICU 54 */
private static final int IX_JG_LIMIT2 = 7;
private static final int IX_MAX_VALUES = 15;
private static final int IX_TOP = 16;
// definitions for 16-bit bidi/shaping properties word ----------------- ***
/* CLASS_SHIFT=0, */
/* bidi class: 5 bits (4..0) */
private static final int JT_SHIFT = 5; /* joining type: 3 bits (7..5) */
private static final int BPT_SHIFT = 8; /* Bidi_Paired_Bracket_Type(bpt): 2 bits (9..8) */
private static final int JOIN_CONTROL_SHIFT = 10;
private static final int BIDI_CONTROL_SHIFT = 11;
private static final int IS_MIRRORED_SHIFT = 12; /* 'is mirrored' */
private static final int MIRROR_DELTA_SHIFT = 13; /* bidi mirroring delta: 3 bits (15..13) */
private static final int MAX_JG_SHIFT =
16; /* max JG value in indexes[MAX_VALUES_INDEX] bits 23..16 */
private static final int CLASS_MASK = 0x0000001f;
private static final int JT_MASK = 0x000000e0;
private static final int BPT_MASK = 0x00000300;
private static final int MAX_JG_MASK = 0x00ff0000;
private static final int getClassFromProps(int props) {
return props & CLASS_MASK;
}
private static final boolean getFlagFromProps(int props, int shift) {
return ((props >> shift) & 1) != 0;
}
private static final int getMirrorDeltaFromProps(int props) {
return (short) props >> MIRROR_DELTA_SHIFT;
}
private static final int ESC_MIRROR_DELTA = -4;
// private static final int MIN_MIRROR_DELTA=-3;
// private static final int MAX_MIRROR_DELTA=3;
// definitions for 32-bit mirror table entry --------------------------- ***
/* the source Unicode code point takes 21 bits (20..0) */
private static final int MIRROR_INDEX_SHIFT = 21;
// private static final int MAX_MIRROR_INDEX=0x7ff;
private static final int getMirrorCodePoint(int m) {
return m & 0x1fffff;
}
private static final int getMirrorIndex(int m) {
return m >>> MIRROR_INDEX_SHIFT;
}
/*
* public singleton instance
*/
public static final UBiDiProps INSTANCE;
// This static initializer block must be placed after
// other static member initialization
static {
try {
INSTANCE = new UBiDiProps();
} catch (IOException e) {
throw new ICUUncheckedIOException(e);
}
}
}