home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Java 1.2 How-To
/
JavaHowTo.iso
/
3rdParty
/
jbuilder
/
unsupported
/
JDK1.2beta3
/
SOURCE
/
SRC.ZIP
/
java
/
awt
/
font
/
IncrementalBidi.java
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
UTF-8
Wrap
Java Source
|
1998-03-20
|
79.6 KB
|
2,334 lines
/*
* @(#)IncrementalBidi.java 1.10 98/03/18
*
* Copyright 1997, 1998 by Sun Microsystems, Inc.,
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of Sun Microsystems, Inc. ("Confidential Information"). You
* shall not disclose such Confidential Information and shall use
* it only in accordance with the terms of the license agreement
* you entered into with Sun.
*/
/*
* (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
*
* The original version of this source code and documentation is
* copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
* of IBM. These materials are provided under terms of a License
* Agreement between Taligent and Sun. This technology is protected
* by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.awt.font;
import java.text.CharacterIterator;
import java.text.AttributedCharacterIterator;
import java.text.AttributeSet;
// Debugging
import java.io.PrintStream;
/**
* This version has been modified to support a modified bidi algorithm that
* increases the locality of operations on the text.
*
* <p>1. It treats roman numeric text three different ways:
*
* <p>STRONG_ROMAN - EN remains EN, and is treated as L when resolving neutrals
* <br>STRONG_ARABIC - EN becomes AN, and is treated as R when resolving
* neutrals
* <br>WEAK_ARABIC - EN remains EN, and is treated as R when resolving neutrals
*
* <p>The client sees a style that applies to characters that can have one of
* these three possible values. Bidi interprets these styles to override the
* default direction array.
* Clients calling bidi using the low level api that takes a direction and
* level array should initialize the values of the arrays similarly.
*
* <p>This affects bidi rules are P0 and N3, as follows:
*
* <p>Rule P0 is disabled. EN values that should become AN must be styled using
* STRONG_ARABIC.
* <p>Rule N3 (a) and (b) are disabled. EN values that should be treated as R
* must be styled using WEAK_ARABIC. Those values that should be treated as L
* may be styled STRONG_ROMAN
* or left unstyled.
*
* <p>2. It does not default run direction.
*
* <p> The client sees a style that applies to paragraphs that can have either
* of two values, RUN_DIRECTION_LTR or RUN_DIRECTION_RTL. The client applies
* the appropriate style
* to the paragraph text.
*
* <p>This disables bidi rules B1 and B2.
*
* <p>The default rules <b>are applied</b> when constructing a bidi object from
* unstyled text (String or CharacterIterator) or from styled text
* (AttributedCharacterIterator) when embedding styles are absent. These rules
* are applied <b>only at construction time</b>, and so this behavior is only
* useful for static text. Calling insert or delete on the bidi object will
* not reapply these rules.
*/
final class IncrementalBidi {
/* All public so clients can construct any direction array. */
public static final byte L = 0; /* left to right (strong) */
public static final byte R = 1; /* right to left (strong) */
public static final byte EN = 2; /* european number (weak) */
public static final byte ES = 3; /* european number separator (weak) */
public static final byte ET = 4; /* european number terminator (weak) */
public static final byte AN = 5; /* arabic number (weak) */
public static final byte CS = 6; /* common number separator (weak) */
public static final byte B = 7; /* block separator */
public static final byte S = 8; /* segment separator */
public static final byte WS = 9; /* whitespace */
public static final byte ON = 10; /* other neutral */
public static final byte WA = 11; /* weak arabic variant of EN */
/* These are used by the utility that strips explicit formatting codes. When editing,
* these just can't be around. Otherwise client editors need to be smart and delete the
* matching codes. I'd rather convert going in and out of the editor, and forget
* about keeping in synch with the original source while editing. */
private static final char LRE = 0x202A; /* left to right embedding */
private static final char RLE = 0x202B; /* right to left embedding */
private static final char PDF = 0x202C; /* pop directional formatting */
private static final char LRO = 0x202D; /* left to right override */
private static final char RLO = 0x202E; /* right to left override */
private static final char MIN_EXPLICIT_CODE = LRE; // bounds of range of explicit formatting codes
private static final char MAX_EXPLICIT_CODE = RLO;
/* These codes are not stripped. */
private static final char LRM = 0x200E; /* left to right mark */
private static final char RLM = 0x200F; /* right to left mark */
/* The embedding/override level stack limit */
private static final char NUMLEVELS = 16;
/* The range of Arabic unicode values, used for default style application. */
private static final char ARABIC_BLOCK_START = 0x0600;
private static final char ARABIC_BLOCK_LIMIT = 0x0700;
// !!! debug flags, remove when ship
static boolean TEST = false;
static boolean DEBUGGING = false;
static PrintStream debugStream = null;
/* State. */
private int length; // the length of the paragraph
private boolean canonical; // true if all levels are zero
private boolean ltr; // the base nesting level (line direction)
private GapArrayOfByte srcdirs; // the source directional formatting codes
private GapArrayOfByte srclevels; // the source nesting levels
private GapArrayOfByte dirs; // the resulting directional formatting codes
private GapArrayOfByte levels; // the resulting levels
/**
* Return the default direction class for this character.
*/
public static byte getDirectionClass(char c) {
if (TEST) {
byte dir;
if (((c >= 'a') && (c <= 'z')) || c == LRO || c == LRE || c == LRM)
dir = L;
else if (((c >= 'A') && (c <= 'Z')) || c == RLO || c == RLE || c == RLM)
dir = R;
else if ((c >= '0') && (c <= '9')) // !!! no arabic numerals for now
dir = EN;
else if ((c == '.') || (c == '/'))
dir = ES;
else if ((c == '$') || (c == '+'))
dir = ET;
else if ((c >= '5') && (c <= '9'))
dir = AN;
else if ((c == ',') || (c == ':'))
dir = CS;
else if (c == '\r')
dir = B;
else if (c == '\t')
dir = S;
else if (c == ' ')
dir = WS;
else
dir = ON;
return dir;
} else {
// real code
return fgDirectionClassArray.elementAt(c);
}
}
//
// Helper functions
//
/**
* Return true if the string contains any right-to-left characters that
* might require bidi processing.
*/
private static boolean containsBidi(String str) {
// return containsBidi(str.toCharArray(), 0, str.length());
int start = 0;
int limit = str.length();
if (TEST) {
while (start < limit) {
char c = str.charAt(start);
if (c <= 'Z' && c >= 'A') {
byte dir = getDirectionClass(c);
if (dir == R || dir == AN)
return true;
}
++start;
}
} else {
while (start < limit) {
char c = str.charAt(start);
if (c > 0x00ff) {
byte dir = getDirectionClass(c);
if (dir == R || dir == AN)
return true;
}
++start;
}
}
return false;
}
/**
* Return true if the iterator contains any right-to-left characters in its
* range that might require bidi processing.
*/
private static boolean containsBidi(CharacterIterator iter)
{
char[] text = toCharArray(iter);
return containsBidi(text, 0, text.length);
}
/**
* Return true if the text range contains any right-to-left characters that
* might require bidi processing.
*/
private static boolean containsBidi(char[] text, int start, int limit)
{
if (TEST) {
while (start < limit) {
char c = text[start];
if (c <= 'Z' && c >= 'A') {
byte dir = getDirectionClass(c);
if (dir == R || dir == AN)
return true;
}
++start;
}
} else {
while (start < limit) {
char c = text[start];
if (c > 0x00ff) {
byte dir = getDirectionClass(c);
if (dir == R || dir == AN)
return true;
}
++start;
}
}
return false;
}
/**
* Return true if the styled string contains any right-to-left characters
* that might require bidi processing, or any bidirectional styles that
* might require bidi processing.
*/
private static boolean containsBidi(StyledString sstr) {
// !!! package access to internal array removed by jk
// See StyledString.getChars()
return containsBidi(sstr.getChars(), 0, sstr.getChars().length) ||
attrContainsBidi(new StyledStringIterator(sstr));
}
/**
* Return true if the iterator contains any right-to-left characters in its
* range that might require bidi processing, or any bidirectional styles
* that might require bidi processing.
*/
private static boolean containsBidi(AttributedCharacterIterator iter) {
return containsBidi((CharacterIterator)iter) || attrContainsBidi(iter);
}
/**
* Return true if the attribute set indicates bidi processing is required.
*/
private static boolean attrContainsBidi(AttributeSet attrs) {
if (attrs != null) {
if (TextAttributeSet.RUN_DIRECTION_RTL.equals(
attrs.get(TextAttributeSet.RUN_DIRECTION))) {
return true;
}
if (attrs.get(TextAttributeSet.BIDI_EMBEDDING) != null) {
return true;
}
if (attrs.get(TextAttributeSet.BIDI_NUMERIC) != null) {
return true;
}
}
return false;
}
private static boolean attrContainsBidi(AttributedCharacterIterator iter) {
iter.first();
AttributeSet attrs = iter.getAttributes();
if (TextAttributeSet.RUN_DIRECTION_RTL.equals(
attrs.get(TextAttributeSet.RUN_DIRECTION))) {
return true;
}
for (;;) {
if (attrs.get(TextAttributeSet.BIDI_EMBEDDING) != null ||
attrs.get(TextAttributeSet.BIDI_NUMERIC) != null) {
return true;
}
if (iter.setIndex(iter.getRunLimit()) == CharacterIterator.DONE) {
break;
}
attrs = iter.getAttributes();
}
return false;
}
private static char[] toCharArray(CharacterIterator iter) {
int start = iter.getBeginIndex();
int limit = iter.getEndIndex();
char[] text = new char[limit - start];
int n = 0;
for (char c = iter.first(); c != iter.DONE; c = iter.next()) {
text[n++] = c;
}
return text;
}
/**
* Check to see if the paragraph represented by the string requires bidi
* processing, and if so, return a bidi object. Otherwise return null.
*/
public static IncrementalBidi createBidi(String str) {
if (containsBidi(str)) {
return new IncrementalBidi(str.toCharArray(),
0, str.length(), null);
}
return null;
}
/**
* Check to see if the paragraph represented by the iterator's range
* requires bidi processing, and if so, return a bidi object. Otherwise
* return null.
*/
public static IncrementalBidi createBidi(CharacterIterator iter) {
char[] text = toCharArray(iter);
return createBidi(text, 0, text.length, null);
}
/**
* Check to see if the paragraph represented by the iterator's range
* requires bidi processing, and if so, return a bidi object. Otherwise
* return null.
*/
public static IncrementalBidi createBidi(char[] text, int start,
int limit, AttributeSet attrs) {
if (containsBidi(text, start, limit) || attrContainsBidi(attrs)) {
return new IncrementalBidi(text, start, limit, attrs);
}
return null;
}
/**
* Check to see if the paragraph represented by the iterator's range
* requires bidi processing, and if so, return a bidi object. Otherwise
* return null.
*/
public static IncrementalBidi createBidi(AttributedCharacterIterator iter) {
if (containsBidi(iter)) {
return new IncrementalBidi(iter);
}
return null;
}
/**
* Apply default rules for line direction and numeric type. Return
* true if the default line direction is ltr, false otherwise. Generate
* values in the direction array, applying default numeric rules.
*/
private boolean applyDefaultRules(CharacterIterator iter,
byte[] dirArray, byte[] lvlArray) {
char[] text = toCharArray(iter);
return applyDefaultRules(text, 0, text.length, dirArray, lvlArray,
null);
}
private boolean applyDefaultRules(char[] text, int start, int limit,
byte[] dirArray, byte[] lvlArray, AttributeSet attrs) {
boolean ltr = true;
Boolean rd = (Boolean)(attrs != null ?
attrs.get(TextAttributeSet.RUN_DIRECTION) : null);
if (rd != null) {
ltr = TextAttributeSet.RUN_DIRECTION_LTR.equals(rd);
} else {
for (int i = start; i < limit; i++) {
byte dir = getDirectionClass(text[i]);
if (dir == L || dir == R) {
ltr = dir == L;
break;
}
}
}
int emb = -1;
Integer be = (Integer)(attrs != null ?
attrs.get(TextAttributeSet.BIDI_EMBEDDING) : null);
if (be != null) {
emb = be.intValue();
if (emb > 0xf) {
byte lvl = (byte)(emb & 0xf);
byte dir = (byte)(emb & 0x1);
for (int i = 0; i < dirArray.length; i++) {
dirArray[i] = dir;
lvlArray[i] = lvl;
}
// !!! early return
return dir == 0;
} else {
byte lvl = (byte)emb;
for (int i = 0; i < lvlArray.length; i++) {
lvlArray[i] = lvl;
}
}
}
Integer bn = (Integer)(attrs != null ?
attrs.get(TextAttributeSet.BIDI_NUMERIC) : null);
if (bn != null) {
byte enConvert = (byte)bn.intValue();
int n = 0;
for (int i = start; i < limit; i++) {
byte dir = getDirectionClass(text[i]);
if (enConvert != EN && dir == EN) {
dir = enConvert;
}
dirArray[n++] = dir;
}
} else {
// apply default numeric rules
// P0. EN following first strong == arabic (R) -> AN
// N3. EN following first strong == R (not arabic) -> WA
byte enConvert = WA;
boolean convert = ltr == false;
int n = 0;
for (int i = start; i < limit; i++) {
char c = text[i];
byte dir = getDirectionClass(c);
if (dir == R) {
convert = true;
if (c >= ARABIC_BLOCK_START && c < ARABIC_BLOCK_LIMIT)
enConvert = AN;
else
enConvert = WA;
} else if (dir == L) {
convert = false;
} else if (dir == EN) {
if (convert)
dir = enConvert;
}
dirArray[n++] = dir;
}
}
if (ltr == false && emb < 1)
for (int i = 0; i < lvlArray.length; i++)
lvlArray[i] = 1;
return ltr;
}
/**
* Construct an incremental bidi with no text, but with the indicated line
* direction. This line direction is fixed for all subsequent insertions
* and deletions from the bidi.
*/
public IncrementalBidi(boolean ltr) {
reset(new byte[] {}, new byte[] {}, ltr, 0);
}
/**
* Construct from a paragraph represented by the string, applying default
* rules.
*
* The default rules for line direction and numeric type are applied.
* To avoid this, clients should call the styled text interface, setting
* the run direction style to indicate default processing is complete.
*
* @param str the paragraph for which to generate bidi information.
*/
public IncrementalBidi(String str) {
this(str.toCharArray(), 0, str.length(), null);
}
/**
* Construct from a text array.
*/
public IncrementalBidi(char[] text, int start, int limit, AttributeSet attrs) {
int len = limit - start;
byte[] dirArray = new byte[len];
byte[] lvlArray = new byte[len];
boolean ltr = applyDefaultRules(text, start, limit, dirArray, lvlArray, attrs);
reset(dirArray, lvlArray, ltr, len);
}
/**
* Construct from styled text.
*
* @param text the styled text to use. The entire current range will be
* considered to be one
* paragraph.
*/
public IncrementalBidi(AttributedCharacterIterator text) {
int begin = text.getBeginIndex();
int end = text.getEndIndex();
int len = end - begin;
boolean ltr = true;
byte[] dirArray = new byte[len];
byte[] lvlArray = new byte[len];
boolean appliedDefaults = false;
char c = text.first();
AttributeSet ff = text.getAttributes();
Boolean runDirection = (Boolean)ff.get(TextAttributeSet.RUN_DIRECTION);
if (runDirection != null) {
ltr = TextAttributeSet.RUN_DIRECTION_LTR.equals(runDirection);
} else {
ltr = applyDefaultRules(text, dirArray, lvlArray);
c = text.first(); // reset iterator
appliedDefaults = true;
}
boolean embeddingOverride = false; // no override
byte embeddingOverrideDir = L;
boolean numericOverride = false; // EN unchanged
byte numericOverrideDir = AN;
byte baseLevel = ltr ? L : R;
byte level = baseLevel;
int index = 0;
while (index < len) {
ff = text.getAttributes();
int indexLimit = text.getRunLimit() - begin;
Integer embedding = (Integer)ff.get(TextAttributeSet.BIDI_EMBEDDING);
if (embedding != null) {
level = (byte)embedding.intValue();
if (level > 0xf) {
embeddingOverride = true;
// (level & 0x1) == 0 ? L : R;
embeddingOverrideDir = (byte)(level & 0x1);
level = (byte)(level & 0xf);
} else {
embeddingOverride = false;
}
} else {
embeddingOverride = false;
level = baseLevel;
}
Integer numeric = (Integer)ff.get(TextAttributeSet.BIDI_NUMERIC);
if (numeric != null) {
numericOverrideDir =(byte)numeric.intValue();
if (numericOverrideDir != EN) {
numericOverride = true;
} else {
numericOverride = false;
}
} else {
numericOverride = false;
}
while (index < indexLimit) {
if (embeddingOverride) {
dirArray[index] = embeddingOverrideDir;
} else if (numericOverride) {
if (appliedDefaults) {
if (dirArray[index] == EN) {
dirArray[index] = numericOverrideDir;
}
} else {
byte direction = getDirectionClass(c);
if (direction == EN) {
direction = numericOverrideDir;
}
dirArray[index] = direction;
}
} else {
if (!appliedDefaults) {
dirArray[index] = getDirectionClass(c);
}
}
lvlArray[index] = level;
index++;
c = text.next();
}
}
reset(dirArray, lvlArray, ltr, len);
}
/**
* Construct from a dirs and levels array.
*
* This is the low-level constructor, the raw data is passed directly into
* the bidi object. The bidi object does not modify or keep a reference
* to these arrays.
*
* @param dirArray the array of character bidi direction codes.
* @param levelArray the array of embedding levels. It contains
* values from 0 to 15 indicating the embedding level for the corresponding
* character. Overrides have already been incorporated into the dirs array.
* If ltr is false, all levels must be greater than zero.
* @param ltr true if the text is left to right (top to bottom), false
* otherwise.
* @param length the length of the paragraph. It must be less than or
* equal to the length of each array.
*/
public IncrementalBidi(byte[] dirArray, byte[] levelArray, boolean ltr,
int length) {
reset(dirArray, levelArray, ltr, length);
}
/**
* Reset the bidi information.
*
* Constructors call this to complete initialization of the bidi object.
*
*/
private void reset(byte[] dirArray, byte[] lvlArray, boolean ltr,
int length)
{
if (dirArray == null || lvlArray == null ||
dirArray.length < length || lvlArray.length < length) {
throw new Error("invalid arguments");
}
this.length = length;
this.canonical = ltr;
this.ltr = ltr;
// !!! could use just one 4x array...
this.srcdirs = new GapArrayOfByte(dirArray, 0, length);
this.srclevels = new GapArrayOfByte(lvlArray, 0, length);
this.dirs = (GapArrayOfByte)srcdirs.clone();
this.levels = (GapArrayOfByte)srclevels.clone();
/*
* this early check for canonical text differs from the public check
* since it is on the source data. This idea is to avoid running
* reapply at all.
*/
for (int i = 0; canonical && i < length; i++)
canonical = dirArray[i] != R && lvlArray[i] == 0;
if (!canonical)
reapply(0, length);
}
/**
* Return true if the bidi is canonical-- the level of each character is
* even. Canonical text is all left-to-right.
*/
public boolean isCanonical() {
return canonical;
}
/** Return true if the bidi is canonical over the range. */
public boolean isCanonical(int start, int limit) {
for (int i = start; i < limit; i++) {
if (levels.at(i) > 0) {
return false;
}
}
return true;
}
/** Reapply the bidi algorithm to the range start-limit */
private void reapply(int start, int limit) {
if (start == limit) {
return;
}
if (DEBUGGING) debug("reapply from " + start + " to " + limit, null);
resolveWeakTypes(start, limit);
if (DEBUGGING) debug("after resolveWeakTypes", null);
resolveNeutralTypes(start, limit);
if (DEBUGGING) debug("after resolveNeutralTypes", null);
resolveImplicitLevels(start, limit);
if (DEBUGGING) debug("after resolveImplicitLevels", null);
canonical = canonical ? isCanonical(start, limit) :
isCanonical(0, length);
}
/*
* Reset the final arrays to the original arrays in preparation for
* calling reapply.
* Will this introduce an apparent embedding boundary at the limits?
* It shouldn't since the algorithm never computes from the resolved
* levels, only the source levels. In the algorithm we're careful-- after
* writing the tests, anyway-- to distinguish the recomputation limits from
* an embedding boundary.
*
* Since reapply is also called when we construct, and the constructor takes
* care of setting up dirs and levels, keep this code here so we don't
* iterate over all the values again.
*/
private void reinit(int start, int limit) {
for (int i = start; i < limit; i++) {
dirs.atPut(i, srcdirs.at(i));
levels.atPut(i, srclevels.at(i));
}
}
// This is used by insert to adjust the range to examine.
private static final boolean canBecomeNeutral[] = {
false, // "L"
false, // "R"
false, // "EN"
true, // "ES"
true, // "ET"
false, // "AN"
true, // "CS"
true, // "B"
true, // "S"
true, // "WS"
true, // "ON"
false // "WA"
};
/**
* Insert a character into the bidi information. The new character is
* inserted
* at pos and has the indicated direction code and level. Determine the
* damage area about pos and reapply bidi to it.
*/
public void insert(int pos, byte dir, byte level) {
srcdirs.insert(pos, dir);
srclevels.insert(pos, level);
dirs.insert(pos, dir);
levels.insert(pos, level);
length += 1;
/*
* Set range to include anything that is or can become a neutral.
* This includes ES, CS, and ET which can become neutral due to weak
* type processing. This takes care of resolveWeakTypes and
* resolveNeutralTypes. ResolveImplicitLevels also needs a range
* adjustment to include the EN after a change, since the level of
* this EN depends on the previous direction.
*/
int start = pos;
int limit = pos + 1;
while (start > 0 && canBecomeNeutral[srcdirs.at(start-1)]) {
--start;
}
while (limit < length && canBecomeNeutral[srcdirs.at(limit)]) {
++limit;
}
if (limit < length && srcdirs.at(limit) == EN) {
limit++;
}
/*
* Reset dir and levels array within range to original values, we'll
* recompute. We must do this because the later parts of the algorithm
* use dirs, which the first part assumes is already initialized.
* Similarly, the resolveImplicitLevels function assumes levels has
* already been initialized and only writes changes to it.
*/
reinit(start, limit);
reapply(start, limit);
}
/**
* Delete a character at pos. Determine the damage area about pos and reapply bidi
* to it.
*/
public void delete(int pos)
{
// see insert for notes on range adjustments
srcdirs.delete(pos, 1);
srclevels.delete(pos, 1);
dirs.delete(pos, 1);
levels.delete(pos, 1);
length -= 1;
int start = pos;
int limit = pos;
while (start > 0 && canBecomeNeutral[srcdirs.at(start-1)]) --start;
while (limit < length && canBecomeNeutral[srcdirs.at(limit)]) ++limit;
if (limit < length && srcdirs.at(limit) == EN)
limit++;
reinit(start, limit);
reapply(start, limit);
}
// This resolves serially in order from left to right, with the results of previous changes
// taken into account for later characters. So, for example, a series of ET's after an EN
// will all change to EN, since once the first ET changes to EN, it is then treated as EN
// for transforming the following ET, and so on. It will also process ETs before EN by
// scanning forward across runs of ET and checking the following character.
//
// Since this processes within the range only, the range must include any ET values
// that could be transformed by this function. Otherwise, for example, a start
// at an EN that follows an ET will not transform the ET, although a start at the ET
// would. When applying incrementally, start and limit should be past any runs of ET
// about the modification position.
//
// !!! This does not take embedded levels into account. Should it?
private void resolveWeakTypes(int start, int limit)
{
int i = start;
byte prev = (i == 0) ? -1 : srcdirs.at(i - 1);
byte cur = srcdirs.at(i);
while (i < limit) {
int ii = i + 1;
byte next = (ii == length) ? -1 : srcdirs.at(ii);
byte ncur = cur;
switch (cur) {
case L:
case R:
break;
case ES:
if ((prev == EN || prev == WA) && (next == EN || next == WA))
ncur = EN;
else
ncur = ON;
break;
case CS:
if ((prev == EN || prev == WA) && (next == EN || next == WA))
ncur = EN;
else if (prev == AN && next == AN)
ncur = AN;
else
ncur = ON;
break;
case ET:
if (prev == EN || prev == WA || next == EN || next == WA) {
ncur = EN;
} else if (next == ET) { // forward scan to handle ET ET EN
for (int j = ii + 1; j < limit; ++j) {
byte dir = srcdirs.at(j);
if (dir == ET)
continue;
if (dir == EN || dir == WA) {
while (ii < j)
dirs.atPut(ii++, EN);
ncur = EN;
next = dir;
}
break;
}
} else {
ncur = ON;
}
break;
default:
break;
}
if (ncur != cur)
dirs.atPut(i, ncur);
i = ii;
prev = ncur;
cur = next;
}
}
// According to Mark, this operation should never span a level boundary. The start and end
// of the level should be treated like sot and eot, with the base direction the direction of the
// level.
//
// When applying incrementally, start and limit should be past any runs of neutrals about
// the modification position.
private static final boolean isNeutral[] = {
false, // "L"
false, // "R"
false, // "EN"
false, // "ES"
false, // "ET"
false, // "AN"
false, // "CS"
true, // "B"
true, // "S"
true, // "WS"
true, // "ON"
false // "WA"
};
private void resolveNeutralTypes(int start, int limit)
{
int i = start;
while (i < limit) {
byte tempBaseLevel = srclevels.at(i);
byte tempBaseDir = ((tempBaseLevel & 0x1) == 0) ? L : R;
int eot = i + 1;
while (eot < limit && srclevels.at(eot) == tempBaseLevel)
eot++;
byte last = tempBaseDir;
// if we're starting a level boundary, the above value for last is correct.
// otherwise last depends on the direction type of the previous character.
// for for the very first pass, set 'last' correctly
if (i == start && i > 0 && srclevels.at(i-1) == tempBaseLevel) {
switch (dirs.at(i-1)) {
case L:
case EN:
last = L;
break;
case R:
case WA:
case AN:
last = R;
break;
}
}
while (i < eot) {
byte dir = dirs.at(i);
if (isNeutral[dir]) { // any neutral
int j = i + 1;
while (j < eot && isNeutral[dir = dirs.at(j)])
j++;
byte next = tempBaseDir;
if (j < eot || (eot == limit && limit < length && srclevels.at(eot) == tempBaseLevel)) {
if (j == eot)
dir = dirs.at(eot);
switch(dir) {
case L:
case EN:
next = L;
break;
case R:
case WA:
case AN:
next = R;
break;
}
}
if (last != next)
last = tempBaseDir;
while (i < j) {
dirs.atPut(i, last);
i++;
}
if (i == eot)
break;
}
switch (dir) {
case L:
case EN:
last = L;
break;
case R:
case WA:
case AN:
last = R;
break;
}
i++;
}
}
}
// Mark says to not use "global direction" but instead use the resolved level.
// EN processing is influenced by level boundaries.
// this also processes segment and paragraph separator directions
//
// Implicit level processing looks at the previous position in some cases
// involving EN. This means that a change to that position can affect the
// level of a following EN. The reapply range must be expanded to
// account for this case.
//
// This interprets 'end of line' in L1 to mean end of segment. Runs of whitespace at
// the end of the paragraph, and before a block or segment separator, are set to the
// base level.
private void resolveImplicitLevels(int start, int limit) {
byte baselevel = (byte)(ltr ? 0 : 1);
for (int i = start; i < limit; i++) {
byte level = srclevels.at(i);
byte nlevel = level;
switch (dirs.at(i)) {
case L: nlevel = (byte)((level + 1) & 0xe); break;
case R: nlevel = (byte)(level | 0x1); break;
case AN: nlevel = (byte)((level + 2) & 0xe); break;
case WA:
case EN:
if ((level & 0x1) != 0) {
nlevel += 1;
} else if (i == 0 || srclevels.at(i-1) != level) {
// !!!
nlevel += 2;
} else {
byte dir = dirs.at(i-1);
if (dir == EN || dir == WA) {
nlevel = levels.at(i-1);
} else if (dir != L) {
nlevel += 2;
}
}
break;
case B:
case S: nlevel = baselevel;
// scan over preceeding spaces and set them to baselevel too
for (int j = i - 1; j >= start && dirs.at(j) == WS; --j) {
levels.atPut(j, baselevel);
}
break;
}
if (nlevel < NUMLEVELS && nlevel != level) {
levels.atPut(i, nlevel);
}
}
if (limit == length || dirs.at(limit) == B || dirs.at(limit) == S) {
for (int j = limit - 1; j >= start && dirs.at(j) == WS; --j) {
levels.atPut(j, baselevel);
}
}
}
/**
* Return a mapping of the entire bidirectional information from logical to
* visual position.
*/
public int[] createLogicalToVisualMap() {
return createLogicalToVisualMap(0, length);
}
/**
* Return a mapping of the subrange of bidirectional information from
* logical to visual position. For example, result[x] = y means that,
* when only the glyphs from logical positions start to limit are
* displayed on a line, the glyph at logical position start + x is at
* visual position y (measuring from the left or top).
*/
public int[] createLogicalToVisualMap(int start, int limit) {
/*
* !!! I'm having trouble thinking through how to generate this
* directly, so for now let's wimp out and invert the vtl map.
* Too bad because this is the map TextLayout wants.
*/
int[] mapping = createVisualToLogicalMap(start, limit);
if (mapping == null) {
return null;
}
for (int i = 0; i < mapping.length; i++) {
mapping[i] -= start;
}
return GlyphSet.getInverseOrder(mapping);
}
/**
* Return a mapping of the entire bidirectional information from visual to
* logical position.
*/
public int[] createVisualToLogicalMap() {
return createVisualToLogicalMap(0, length);
}
/**
* Return a mapping of the subrange of bidirectional information from visual
* to logical position. For example, result[x] = y means that, when only
* the glyphs from logical positions start to limit are displayed on a
* line, the glyph at visual position x (measuring from left or top) is
* logical glyph y.
*
* Whitespace at the end of the range is mapped to the base level,
* implementing the line reordering rules of the bidi algorithm.
*
* @param start the start of the logical range for which to generate the
* mapping.
* @param limit the limit of the logical range for which to generate the
* mapping.
*/
public int[] createVisualToLogicalMap(int start, int limit) {
if (levels == null) {
return null;
}
boolean canonical = true;
for (int i = start; canonical && i < limit; i++) {
canonical = levels.at(i) == 0;
}
if (canonical) {
if (DEBUGGING) debugStream.println("*** vtl map canonical from " + start + " to " + limit);
return null;
}
int maplen = limit - start;
int[] mapping = new int[maplen];
// find out how much trailing whitespace there is
// first skip over runs of B and S, then over runs of WS
int ws = 0;
int wsi = limit - 1;
byte d;
for (; (wsi >= start) && ((d = dirs.at(wsi)) == B || d == S);
--wsi)
{
ws++;
}
for (; (wsi >= start) && (dirs.at(wsi) == WS); --wsi) {
ws++;
}
// don't process these values, we'll special case them later
limit -= ws;
int mapstart = ltr ? 0 : ws;
byte lowestOddLevel = (byte)(NUMLEVELS + 1);
byte highestLevel = 0;
// initialize mapping and levels
for (int i = start; i < limit; i++) {
mapping[i - start + mapstart] = i;
byte level = levels.at(i);
if (level > highestLevel) {
highestLevel = level;
}
if (((level & 0x01) != 0) && (level < lowestOddLevel)) {
lowestOddLevel = level;
}
}
while (highestLevel >= lowestOddLevel) {
int i = start;
for (;;) {
while ((i < limit) && (levels.at(i) < highestLevel)) {
i++;
}
int begin = i++;
if (begin == limit) {
break; // no more runs at this level
}
while ((i < limit) && (levels.at(i) >= highestLevel)) {
i++;
}
int end = i - 1;
begin -= start - mapstart;
end -= start - mapstart;
while (begin < end) {
int temp = mapping[begin];
mapping[begin] = mapping[end];
mapping[end] = temp;
++begin;
--end;
}
}
// debug("after remap "+highestLevel+" "+ mappedString());
--highestLevel;
}
// now let's handle the whitespace
if (ltr) {
for (int i = limit; ws > 0; --ws, ++i) {
mapping[i - start] = i;
}
} else {
while (ws > 0) {
mapping[--ws] = limit++;
}
}
return mapping;
}
/**
* Return true if the base line direction for the paragraph is left to
* right.
*/
public boolean isDirectionLTR() {
return ltr;
}
/*
* Return the level array. If the bidi is canonical, return null.
*/
public byte[] getLevels() {
if (canonical) {
return null;
}
return levels.getArray();
}
/*
public byte[] createLevels(int start, int limit)
{
boolean canonical = true;
for (int i = start; canonical && i < limit; i++)
canonical = levels.at(i) == 0;
if (canonical) {
if (DEBUGGING) debugStream.println("*** levels canonical from " + start + " to " + limit);
return null;
}
int levlen = limit - start;
byte[] newlevels = new byte[levlen];
levels.writeout(start, newlevels, 0, levlen);
// set trailing whitespace to base level. Don't worry about
// extra work if this is a lower odd level than the ideal odd
// level for this line, this situation won't happen often.
byte baseLevel = (byte)(ltr ? 0 : 1);
for (int i = limit - 1; (i >= start) && (dirs.at(i) == WS); --i)
newlevels[i - start] = baseLevel;
return newlevels;
}
*/
/**
* Return true if there are any embedding or override codes in the text.
*/
static boolean containsExplicitFormatting(CharacterIterator iter)
{
for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
if ((c >= MIN_EXPLICIT_CODE) && (c <= MAX_EXPLICIT_CODE))
return true;
}
return false;
}
/**
* Return true if an analysis of the text reveals a left to right run
* direction.
*/
static boolean defaultRunIsLeftToRight(AttributedCharacterIterator iter) {
boolean ltr = true;
for (char c = iter.first();
c != CharacterIterator.DONE;
c = iter.next()) {
byte dir = getDirectionClass(c);
if (dir == L || dir == R) {
ltr = dir == L;
break;
}
}
return ltr;
}
//
// debugging code. package access for tests.
//
static final char[] digits = "0123456789abcde".toCharArray();
static String lvlString(byte[] lvlArray) {
if (lvlArray == null) {
return null;
}
StringBuffer buf = new StringBuffer(lvlArray.length);
for (int i = 0; i < lvlArray.length; i++) {
buf.append(digits[lvlArray[i]]);
}
return buf.toString();
}
static final char[] dircodes = "lr#,.A:bswna".toCharArray();
static String dirString(byte[] dirArray) {
if (dirArray == null) {
return null;
}
StringBuffer buf = new StringBuffer(dirArray.length);
for (int i = 0; i < dirArray.length; i++) {
buf.append(dircodes[dirArray[i]]);
}
return buf.toString();
}
// output a message and the current state of the algorithm
// text, if present, is the string the bidi is ostensibly working on
void debug(String message, String text) {
debugStream.println(message);
debugStream.println("length: " + length + ", ltr: " + ltr);
if (text != null) {
debugStream.println("string: " + text);
}
byte[] temp = new byte[length];
srcdirs.writeout(0, temp, 0, length);
debugStream.println("srcdirs: " + dirString(temp));
srclevels.writeout(0, temp, 0, length);
debugStream.println("srclvls: " + lvlString(temp));
dirs.writeout(0, temp, 0, length);
debugStream.println("dirs: " + dirString(temp));
levels.writeout(0, temp, 0, length);
debugStream.println("levels: " + lvlString(temp));
if (text != null) {
String result = null;
int[] vtl = createVisualToLogicalMap();
if (vtl == null) {
result = text;
} else {
StringBuffer buffer = new StringBuffer(vtl.length);
for (int i = 0; i < vtl.length; i++) {
char c = text.charAt(vtl[i]);
if ((levels.at(vtl[i]) & 0x1) != 0) {
buffer.append(c /* mappedChar(c) */);
} else {
buffer.append(c);
}
}
result = buffer.toString();
}
debugStream.println("result: " + result);
}
debugStream.println();
}
private static final byte fgCharDirValues[] = {
ON, ON, ON, ON, ON, ON, ON, ON, ON, S, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, WS, ON, ON, ET, ET, ET, L, ON, ON,
ON, ON, ET, CS, ET, ES, ES, EN, EN, EN,
EN, EN, EN, EN, EN, EN, EN, CS, ON, ON,
ON, ON, ON, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
ON, ON, ON, ON, ON, ON, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, WS, ON, ET, ET, ET, ET,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ET, ET, EN, EN, ON, ON, ON, ON, ON, EN,
ON, ON, ON, ON, ON, ON, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, ON, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
ON, ON, ON, ON, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, ON, ON, ON, ON, ON, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, ON, L, L, L,
L, L, L, L, L, L, L, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, L, L, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, L, L, ON, ON, ON,
ON, L, ON, ON, ON, L, ON, ON, ON, ON,
L, L, L, L, L, L, L, ON, L, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
ON, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, ON, L, L, L, L,
L, L, L, ON, ON, ON, L, ON, L, ON,
L, ON, L, ON, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, L, L, L, L, L, L,
L, L, L, L, L, L, ON, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, ON, L, L, L, L, L, L,
L, L, L, L, L, L, ON, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, ON, ON, ON, ON, ON, ON, ON, ON, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, ON, ON, L, L, ON, ON, L,
L, ON, ON, ON, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, L, L, L, L, L, L,
L, L, ON, ON, L, L, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, ON, ON, L, L, L, L, L,
L, L, ON, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, ON, L, ON, ON, ON, ON,
ON, ON, ON, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
ON, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, ON, R, R, R, R, R,
R, R, R, R, R, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, ON, ON, ON, ON, ON, R, R,
R, R, R, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, R, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
R, ON, ON, ON, R, ON, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, ON, ON, ON, ON, ON, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, AN,
AN, AN, AN, AN, AN, AN, AN, AN, AN, ET,
AN, AN, R, ON, ON, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, ON, ON, R, R, R, R, R, ON, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, ON, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, ON, ON, EN, EN, EN,
EN, EN, EN, EN, EN, EN, EN, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, L, L, L, ON, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, L, L, L, L, L, ON,
ON, ON, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, L, L, L, ON, L, L, L, L,
L, L, L, L, ON, ON, L, L, ON, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, L, L, L, L, L, L, L,
ON, L, ON, ON, ON, L, L, L, L, ON,
ON, L, ON, L, L, L, L, L, L, L,
ON, ON, L, L, ON, ON, L, L, L, ON,
ON, ON, ON, ON, ON, ON, ON, ON, L, ON,
ON, ON, ON, L, L, ON, L, L, L, L,
L, ON, ON, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, ON, ON, ON, ON, ON, L,
ON, ON, L, L, L, L, L, L, ON, ON,
ON, ON, L, L, ON, ON, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, ON, L,
L, L, L, L, L, L, ON, L, L, ON,
L, L, ON, L, L, ON, ON, L, ON, L,
L, L, L, L, ON, ON, ON, ON, L, L,
ON, ON, L, L, L, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, L, L, L, L,
ON, L, ON, ON, ON, ON, ON, ON, ON, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, L, L, L, ON, L,
L, L, L, L, L, L, ON, L, ON, L,
L, L, ON, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, ON, L, L, L, L,
L, L, L, ON, L, L, ON, L, L, L,
L, L, ON, ON, L, L, L, L, L, L,
L, L, L, L, ON, L, L, L, ON, L,
L, L, ON, ON, L, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
L, ON, ON, ON, ON, ON, L, L, L, L,
L, L, L, L, L, L, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, L, L, L, ON, L, L, L, L,
L, L, L, L, ON, ON, L, L, ON, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, L, L, L, L, L, L, L,
ON, L, L, ON, ON, L, L, L, L, ON,
ON, L, L, L, L, L, L, L, L, ON,
ON, ON, L, L, ON, ON, L, L, L, ON,
ON, ON, ON, ON, ON, ON, ON, L, L, ON,
ON, ON, ON, L, L, ON, L, L, L, ON,
ON, ON, ON, L, L, L, L, L, L, L,
L, L, L, L, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, L,
L, ON, L, L, L, L, L, L, ON, ON,
ON, L, L, L, ON, L, L, L, L, ON,
ON, ON, L, L, ON, L, ON, L, L, ON,
ON, ON, L, L, ON, ON, ON, L, L, L,
ON, ON, ON, L, L, L, L, L, L, L,
L, ON, L, L, L, ON, ON, ON, ON, L,
L, L, L, L, ON, ON, ON, L, L, L,
ON, L, L, L, L, ON, ON, ON, ON, ON,
ON, ON, ON, ON, L, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, L, L, L, ON, L,
L, L, L, L, L, L, L, ON, L, L,
L, ON, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, ON, L, L, L, L,
L, L, L, L, L, L, ON, L, L, L,
L, L, ON, ON, ON, ON, L, L, L, L,
L, L, L, ON, L, L, L, ON, L, L,
L, L, ON, ON, ON, ON, ON, ON, ON, L,
L, ON, ON, ON, ON, ON, ON, ON, ON, ON,
L, L, ON, ON, ON, ON, L, L, L, L,
L, L, L, L, L, L, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, L, L, ON, L, L, L, L, L,
L, L, L, ON, L, L, L, ON, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, ON, L, L, L, L, L, L, L, L,
L, L, ON, L, L, L, L, L, ON, ON,
ON, ON, L, L, L, L, L, L, L, ON,
L, L, L, ON, L, L, L, L, ON, ON,
ON, ON, ON, ON, ON, L, L, ON, ON, ON,
ON, ON, ON, ON, L, ON, L, L, ON, ON,
ON, ON, L, L, L, L, L, L, L, L,
L, L, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, L, L,
ON, L, L, L, L, L, L, L, L, ON,
L, L, L, ON, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, ON, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, ON, ON, ON, ON, L, L,
L, L, L, L, ON, ON, L, L, L, ON,
L, L, L, L, ON, ON, ON, ON, ON, ON,
ON, ON, ON, L, ON, ON, ON, ON, ON, ON,
ON, ON, L, L, ON, ON, ON, ON, L, L,
L, L, L, L, L, L, L, L, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, ON, ON, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, L, L, ON, L, ON, ON, L, L, ON,
L, ON, ON, L, ON, ON, ON, ON, ON, ON,
L, L, L, L, ON, L, L, L, L, L,
L, L, ON, L, L, L, ON, L, ON, L,
ON, ON, L, L, ON, L, L, L, L, L,
L, L, L, L, L, L, L, L, ON, L,
L, L, ON, ON, L, L, L, L, L, ON,
L, ON, L, L, L, L, L, L, ON, ON,
L, L, L, L, L, L, L, L, L, L,
ON, ON, L, L, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
ON, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, ON, ON, ON, ON, ON, ON,
ON, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, ON, ON, ON, ON,
L, L, L, L, L, L, ON, L, ON, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
ON, ON, ON, L, L, L, L, L, L, L,
ON, L, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, ON,
ON, ON, ON, L, ON, ON, ON, ON, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, ON, ON,
ON, ON, ON, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, ON, ON, ON, ON, ON,
ON, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, ON,
ON, ON, ON, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, ON, ON, ON, ON, ON, ON, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, ON, ON, L, L, L, L, L, L, ON,
ON, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, ON,
ON, L, L, L, L, L, L, ON, ON, L,
L, L, L, L, L, L, L, ON, L, ON,
L, ON, L, ON, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, ON, ON, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
ON, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, ON, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, ON, ON, L, L, L, L, L, L, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, ON,
ON, L, L, L, ON, L, L, L, L, L,
L, L, L, L, ON, WS, WS, WS, WS, WS,
WS, WS, ES, WS, WS, WS, WS, ON, ON, L,
R, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, B, B, ON, ON, ON,
ON, ON, ON, ET, ET, ET, ET, ET, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, EN, ON, ON,
ON, EN, EN, EN, EN, EN, EN, ET, ET, ON,
ON, ON, ON, EN, EN, EN, EN, EN, EN, EN,
EN, EN, EN, ET, ET, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ET, ET, ET, ET, ET,
ET, ET, ET, ET, ET, ET, ET, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ET, ET, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, ON, ON, ON, ON, ON, WS, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, ON, ON, ON, ON,
L, L, L, L, L, L, ON, ON, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, ON, ON, ON, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, ON, ON, ON,
ON, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
ON, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, ON, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, ON, ON, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, L, L, L, L, L, L,
L, L, L, L, L, L, ON, ON, ON, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, ON, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, ON, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, L, L, L, L, L, L,
L, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, L, L, L, L, L, ON, ON,
ON, ON, ON, ON, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, ON,
R, R, R, R, R, ON, R, ON, R, R,
ON, R, R, ON, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, ON,
ON, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, R, R, R, R, R,
R, R, R, R, R, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, R, R, R, R, R,
R, R, R, R, R, R, R, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, R,
R, R, ON, R, ON, R, R, R, R, R,
R, R, R, R, R, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, EN, EN, EN, EN, EN, EN, EN, EN, EN,
EN, ON, ON, ON, ON, ON, ON, ON, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, ON, ON, ON, ON, ON, ON,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L, L, ON,
ON, ON, L, L, L, L, L, L, ON, ON,
L, L, L, L, L, L, ON, ON, L, L,
L, L, L, L, ON, ON, L, L, L, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, ON, ON, ON, ON, ON, ON, ON,
ON, ON, ON, 10
};
private static final short fgCharDirIndices[] = {
0, 123, 243, 253, 375, 462, 590, 717, 844, 965, 1087,
1207, 1324, 1436, 1558, 1558, 1558, 1558, 1685, 1812, 1938,
2065, 2192, 2318, 2445, 2571, 2697, 1558, 2824, 2951, 3079,
3195, 1558, 3291, 3419, 3474, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 243,
3602, 3730, 3858, 3986, 4114, 4212, 4337, 4447, 1558, 4521,
1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 1558, 1558, 4649, 4756, 4883, 4996, 5124,
5251, 252, 5379, 1558, 1558, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 1558, 1558, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 5507, 1558,
1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558, 1558,
1558, 1558, 1558, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
5509, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
243, 243, 243, 243, 243, 243, 243, 243, 243, 5637,
1558, 5765, 5843, 5926, 5926, 5992, 6104, 6228, 5929, 6356,
6457
};
private static final CompactByteArray fgDirectionClassArray
= new CompactByteArray(fgCharDirIndices, fgCharDirValues);
}
/**
* Gap storage optimized for repeated insertion.
*/
final class GapArrayOfByte implements Cloneable {
private byte[] array;
private int count;
private int gap;
/** Construct a new gap array with room for count elements. */
public GapArrayOfByte(int count) {
array = new byte[count];
count = count;
}
/** Construct a new gap array with the provided src data. */
public GapArrayOfByte(byte[] src) {
array = (byte[])src.clone();
count = src.length;
gap = src.length;
}
/**
* Construct a new gap array with a subrange of the provided src data.
*/
public GapArrayOfByte(byte[] src, int spos, int slen) {
array = new byte[slen];
readin(src, spos, slen, src.length, src.length);
}
/**
* Construct a new gap array with a subrange of the provided gap array.
*/
public GapArrayOfByte(GapArrayOfByte src, int spos, int slen) {
array = new byte[slen];
readin(src.array, spos, slen, src.gap, src.count);
}
/**
* Remove gap from array and return it. Callers should release the array
* before subsequent calls to this object.
*/
public byte[] getArray() {
if (gap < count) { // can't be > count!
int len = count - gap;
System.arraycopy(array, array.length - len, array, gap, len);
gap = count;
}
return array;
}
/** Deep clone the array. */
public Object clone() {
GapArrayOfByte result = null;
try {
result = (GapArrayOfByte)super.clone();
}
catch (CloneNotSupportedException e) {
}
result.array = (byte[])array.clone();
return result;
}
/** Insert a single source element at pos. */
public void insert(int pos, byte src) {
realloc(pos, 0, 1);
array[gap] = src;
gap++;
count++;
}
/** Insert slen elements at pos starting from spos in src. */
public void insert(int pos, byte[] src, int spos, int slen) {
realloc(pos, 0, slen);
readin(src, spos, slen, src.length, src.length);
}
/** Insert slen elements at pos starting from spos in src. */
public void insert(int pos, GapArrayOfByte src, int spos, int slen) {
realloc(pos, 0, slen);
readin(src.array, spos, slen, src.gap, src.count);
}
/** Delete len elements at pos. */
public void delete(int pos, int len) {
if (pos <= gap && pos + len >= gap) {
gap = pos;
count -= len;
} else {
realloc(pos, len, 0);
}
}
/** Replace dlen elements at dpos with slen elements from src at spos. */
public void replace(int dpos, int dlen, byte[] src, int spos, int slen) {
realloc(dpos, dlen, slen);
readin(src, spos, slen, src.length, src.length);
}
/** Replace dlen elements at dpos with slen elements from src at spos. */
public void replace(int dpos, int dlen, GapArrayOfByte src, int spos,
int slen) {
realloc(dpos, dlen, slen);
readin(src.array, spos, slen, src.gap, src.count);
}
/** Return the element at pos. */
public byte at(int pos) {
if (pos >= gap) {
int delta = array.length - count;
pos += delta;
}
return array[pos];
}
/** Set the element at pos to val. */
public void atPut(int pos, byte val) {
if (pos >= gap) {
int delta = array.length - count;
pos += delta;
}
array[pos] = val;
}
/** Copy count elements starting at pos to dest starting at dpos. */
public void writeout(int spos, byte[] dst, int dpos, int len) {
if (spos < gap) {
int blen = Math.min(len, gap - spos);
System.arraycopy(array, spos, dst, dpos, blen);
len -= blen;
spos += blen;
dpos += blen;
}
if (len > 0) {
System.arraycopy(array, spos + array.length - count, dst, dpos,
len);
}
}
/**
* Copy slen elements from gap storage src starting at spos. Elements
* are inserted at gap, src has a gap at sgap and a count of scnt.
*/
private void readin(byte[] src, int spos, int slen, int sgap, int scnt) {
count += slen;
if (spos < sgap) { // copy data before gap
int blen = Math.min(slen, sgap - spos);
System.arraycopy(src, spos, array, gap, blen);
gap += blen;
slen -= blen;
}
if (slen > 0) { // copy remaining data after gap
System.arraycopy(src, sgap + src.length - scnt, array, gap, slen);
gap += slen;
}
}
/**
* Reallocate the storage to move the gap to dpos, delete dlen existing
* elements, and reserve space for slen new elements.
*/
private void realloc(int dpos, int dlen, int slen) {
if (dpos > gap || dpos + dlen < gap ||
count + slen - dlen > array.length) {
int ncount = count + slen - dlen;
byte[] newarray = array;
if (ncount > array.length) {
// assume increments are small
newarray = new byte[ncount + 64];
int hc = Math.min(dpos, gap); // count of elements at head
System.arraycopy(array, 0, newarray, 0, hc);
// count of elements at tail
int tc = count - Math.max(dpos + dlen, gap);
System.arraycopy(array, array.length - tc, newarray,
newarray.length - tc, tc);
}
if (dpos > gap) {
int space = array.length - count;
System.arraycopy(array, gap + space, newarray, gap, dpos - gap);
} else {
int dend = dpos + dlen;
if (dend < gap) {
int nspace = newarray.length - count;
System.arraycopy(array, dend, newarray, dend + nspace,
gap - dend);
}
}
array = newarray;
}
gap = dpos;
count -= dlen;
}
}