/*
* Copyright (C) 2008-2012 OMRON SOFTWARE Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jp.co.omronsoft.openwnn;
import java.util.Iterator;
import java.util.ArrayList;
import android.util.Log;
/**
* The container class of composing string.
*
* This interface is for the class includes information about the
* input string, the converted string and its decoration.
* {@link LetterConverter} and {@link WnnEngine} get the input string from it, and
* store the converted string into it.
*
* @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD. All Rights Reserved.
*/
public class ComposingText {
/**
* Text layer 0.
* <br>
* This text layer holds key strokes.<br>
* (ex) Romaji in Japanese. Parts of Hangul in Korean.
*/
public static final int LAYER0 = 0;
/**
* Text layer 1.
* <br>
* This text layer holds the result of the letter converter.<br>
* (ex) Hiragana in Japanese. Pinyin in Chinese. Hangul in Korean.
*/
public static final int LAYER1 = 1;
/**
* Text layer 2.
* <br>
* This text layer holds the result of the consecutive clause converter.<br>
* (ex) the result of Kana-to-Kanji conversion in Japanese,
* Pinyin-to-Kanji conversion in Chinese, Hangul-to-Hanja conversion in Korean language.
*/
public static final int LAYER2 = 2;
/** Maximum number of layers */
public static final int MAX_LAYER = 3;
/** Composing text's layer data */
protected ArrayList<StrSegment>[] mStringLayer;
/** Cursor position */
protected int[] mCursor;
/**
* Constructor
*/
public ComposingText() {
mStringLayer = new ArrayList[MAX_LAYER];
mCursor = new int[MAX_LAYER];
for (int i = 0; i < MAX_LAYER; i++) {
mStringLayer[i] = new ArrayList<StrSegment>();
mCursor[i] = 0;
}
}
/**
* Output internal information to the log.
*/
public void debugout() {
for (int i = 0; i < MAX_LAYER; i++) {
Log.d("OpenWnn", "ComposingText["+i+"]");
Log.d("OpenWnn", " cur = " + mCursor[i]);
String tmp = "";
for (Iterator<StrSegment> it = mStringLayer[i].iterator(); it.hasNext();) {
StrSegment ss = it.next();
tmp += "(" + ss.string + "," + ss.from + "," + ss.to + ")";
}
Log.d("OpenWnn", " str = "+tmp);
}
}
/**
* Get a {@link StrSegment} at the position specified.
*
* @param layer Layer
* @param pos Position (<0 : the tail segment)
*
* @return The segment; {@code null} if error occurs.
*/
public StrSegment getStrSegment(int layer, int pos) {
try {
ArrayList<StrSegment> strLayer = mStringLayer[layer];
if (pos < 0) {
pos = strLayer.size() - 1;
}
if (pos >= strLayer.size() || pos < 0) {
return null;
}
return strLayer.get(pos);
} catch (Exception ex) {
return null;
}
}
/**
* Convert the range of segments to a string.
*
* @param layer Layer
* @param from Convert range from
* @param to Convert range to
* @return The string converted; {@code null} if error occurs.
*/
public String toString(int layer, int from, int to) {
try {
StringBuffer buf = new StringBuffer();
ArrayList<StrSegment> strLayer = mStringLayer[layer];
for (int i = from; i <= to; i++) {
StrSegment ss = strLayer.get(i);
buf.append(ss.string);
}
return buf.toString();
} catch (Exception ex) {
return null;
}
}
/**
* Convert segments of the layer to a string.
*
* @param layer Layer
* @return The string converted; {@code null} if error occurs.
*/
public String toString(int layer) {
return this.toString(layer, 0, mStringLayer[layer].size() - 1);
}
/**
* Update the upper layer's data.
*
* @param layer The base layer
* @param mod_from Modified from
* @param mod_len Length after modified (# of StrSegments from {@code mod_from})
* @param org_len Length before modified (# of StrSegments from {@code mod_from})
*/
private void modifyUpper(int layer, int mod_from, int mod_len, int org_len) {
if (layer >= MAX_LAYER - 1) {
/* no layer above */
return;
}
int uplayer = layer + 1;
ArrayList<StrSegment> strUplayer = mStringLayer[uplayer];
if (strUplayer.size() <= 0) {
/*
* if there is no element on above layer,
* add a element includes whole elements of the lower layer.
*/
strUplayer.add(new StrSegment(toString(layer), 0, mStringLayer[layer].size() - 1));
modifyUpper(uplayer, 0, 1, 0);
return;
}
int mod_to = mod_from + ((mod_len == 0)? 0 : (mod_len - 1));
int org_to = mod_from + ((org_len == 0)? 0 : (org_len - 1));
StrSegment last = strUplayer.get(strUplayer.size() - 1);
if (last.to < mod_from) {
/* add at the tail */
last.to = mod_to;
last.string = toString(layer, last.from, last.to);
modifyUpper(uplayer, strUplayer.size()-1, 1, 1);
return;
}
int uplayer_mod_from = -1;
int uplayer_org_to = -1;
for (int i = 0; i < strUplayer.size(); i++) {
StrSegment ss = strUplayer.get(i);
if (ss.from > mod_from) {
if (ss.to <= org_to) {
/* the segment is included */
if (uplayer_mod_from < 0) {
uplayer_mod_from = i;
}
uplayer_org_to = i;
} else {
/* included in this segment */
uplayer_org_to = i;
break;
}
} else {
if (org_len == 0 && ss.from == mod_from) {
/* when an element is added */
uplayer_mod_from = i - 1;
uplayer_org_to = i - 1;
break;
} else {
/* start from this segment */
uplayer_mod_from = i;
uplayer_org_to = i;
if (ss.to >= org_to) {
break;
}
}
}
}
int diff = mod_len - org_len;
if (uplayer_mod_from >= 0) {
/* update an element */
StrSegment ss = strUplayer.get(uplayer_mod_from);
int last_to = ss.to;
int next = uplayer_mod_from + 1;
for (int i = next; i <= uplayer_org_to; i++) {
ss = strUplayer.get(next);
if (last_to > ss.to) {
last_to = ss.to;
}
strUplayer.remove(next);
}
ss.to = (last_to < mod_to)? mod_to : (last_to + diff);
ss.string = toString(layer, ss.from, ss.to);
for (int i = next; i < strUplayer.size(); i++) {
ss = strUplayer.get(i);
ss.from += diff;
ss.to += diff;
}
modifyUpper(uplayer, uplayer_mod_from, 1, uplayer_org_to - uplayer_mod_from + 1);
} else {
/* add an element at the head */
StrSegment ss = new StrSegment(toString(layer, mod_from, mod_to),
mod_from, mod_to);
strUplayer.add(0, ss);
for (int i = 1; i < strUplayer.size(); i++) {
ss = strUplayer.get(i);
ss.from += diff;
ss.to += diff;
}
modifyUpper(uplayer, 0, 1, 0);
}
return;
}
/**
* Insert a {@link StrSegment} at the cursor position.
*
* @param layer Layer to insert
* @param str String
**/
public void insertStrSegment(int layer, StrSegment str) {
int cursor = mCursor[layer];
mStringLayer[layer].add(cursor, str);
modifyUpper(layer, cursor, 1, 0);
setCursor(layer, cursor + 1);
}
/**
* Insert a {@link StrSegment} at the cursor position(without merging to the previous segment).
* <p>
* @param layer1 Layer to insert
* @param layer2 Never merge to the previous segment from {@code layer1} to {@code layer2}.
* @param str String
**/
public void insertStrSegment(int layer1, int layer2, StrSegment str) {
mStringLayer[layer1].add(mCursor[layer1], str);
mCursor[layer1]++;
for (int i = layer1 + 1; i <= layer2; i++) {
int pos = mCursor[i-1] - 1;
StrSegment tmp = new StrSegment(str.string, pos, pos);
ArrayList<StrSegment> strLayer = mStringLayer[i];
strLayer.add(mCursor[i], tmp);
mCursor[i]++;
for (int j = mCursor[i]; j < strLayer.size(); j++) {
StrSegment ss = strLayer.get(j);
ss.from++;
ss.to++;
}
}
int cursor = mCursor[layer2];
modifyUpper(layer2, cursor - 1, 1, 0);
setCursor(layer2, cursor);
}
/**
* Replace segments at the range specified.
*
* @param layer Layer
* @param str String segment array to replace
* @param from Replace from
* @param to Replace to
**/
protected void replaceStrSegment0(int layer, StrSegment[] str, int from, int to) {
ArrayList<StrSegment> strLayer = mStringLayer[layer];
if (from < 0 || from > strLayer.size()) {
from = strLayer.size();
}
if (to < 0 || to > strLayer.size()) {
to = strLayer.size();
}
for (int i = from; i <= to; i++) {
strLayer.remove(from);
}
for (int i = str.length - 1; i >= 0; i--) {
strLayer.add(from, str[i]);
}
modifyUpper(layer, from, str.length, to - from + 1);
}
/**
* Replace segments at the range specified.
*
* @param layer Layer
* @param str String segment array to replace
* @param num Size of string segment array
**/
public void replaceStrSegment(int layer, StrSegment[] str, int num) {
int cursor = mCursor[layer];
replaceStrSegment0(layer, str, cursor - num, cursor - 1);
setCursor(layer, cursor + str.length - num);
}
/**
* Replace the segment at the cursor.
*
* @param layer Layer
* @param str String segment to replace
**/
public void replaceStrSegment(int layer, StrSegment[] str) {
int cursor = mCursor[layer];
replaceStrSegment0(layer, str, cursor - 1, cursor - 1);
setCursor(layer, cursor + str.length - 1);
}
/**
* Delete segments.
*
* @param layer Layer
* @param from Delete from
* @param to Delete to
**/
public void deleteStrSegment(int layer, int from, int to) {
int[] fromL = new int[] {-1, -1, -1};
int[] toL = new int[] {-1, -1, -1};
ArrayList<StrSegment> strLayer2 = mStringLayer[2];
ArrayList<StrSegment> strLayer1 = mStringLayer[1];
if (layer == 2) {
fromL[2] = from;
toL[2] = to;
fromL[1] = strLayer2.get(from).from;
toL[1] = strLayer2.get(to).to;
fromL[0] = strLayer1.get(fromL[1]).from;
toL[0] = strLayer1.get(toL[1]).to;
} else if (layer == 1) {
fromL[1] = from;
toL[1] = to;
fromL[0] = strLayer1.get(from).from;
toL[0] = strLayer1.get(to).to;
} else {
fromL[0] = from;
toL[0] = to;
}
int diff = to - from + 1;
for (int lv = 0; lv < MAX_LAYER; lv++) {
if (fromL[lv] >= 0) {
deleteStrSegment0(lv, fromL[lv], toL[lv], diff);
} else {
int boundary_from = -1;
int boundary_to = -1;
ArrayList<StrSegment> strLayer = mStringLayer[lv];
for (int i = 0; i < strLayer.size(); i++) {
StrSegment ss = (StrSegment)strLayer.get(i);
if ((ss.from >= fromL[lv-1] && ss.from <= toL[lv-1]) ||
(ss.to >= fromL[lv-1] && ss.to <= toL[lv-1]) ) {
if (fromL[lv] < 0) {
fromL[lv] = i;
boundary_from = ss.from;
}
toL[lv] = i;
boundary_to = ss.to;
} else if (ss.from <= fromL[lv-1] && ss.to >= toL[lv-1]) {
boundary_from = ss.from;
boundary_to = ss.to;
fromL[lv] = i;
toL[lv] = i;
break;
} else if (ss.from > toL[lv-1]) {
break;
}
}
if (boundary_from != fromL[lv-1] || boundary_to != toL[lv-1]) {
deleteStrSegment0(lv, fromL[lv] + 1, toL[lv], diff);
boundary_to -= diff;
StrSegment[] tmp = new StrSegment[] {
(new StrSegment(toString(lv-1), boundary_from, boundary_to))
};
replaceStrSegment0(lv, tmp, fromL[lv], fromL[lv]);
return;
} else {
deleteStrSegment0(lv, fromL[lv], toL[lv], diff);
}
}
diff = toL[lv] - fromL[lv] + 1;
}
}
/**
* Delete segments (internal method).
*
* @param layer Layer
* @param from Delete from
* @param to Delete to
* @param diff Differential
**/
private void deleteStrSegment0(int layer, int from, int to, int diff) {
ArrayList<StrSegment> strLayer = mStringLayer[layer];
if (diff != 0) {
for (int i = to + 1; i < strLayer.size(); i++) {
StrSegment ss = strLayer.get(i);
ss.from -= diff;
ss.to -= diff;
}
}
for (int i = from; i <= to; i++) {
strLayer.remove(from);
}
}
/**
* Delete a segment at the cursor.
*
* @param layer Layer
* @param rightside {@code true} if direction is rightward at the cursor, {@code false} if direction is leftward at the cursor
* @return The number of string segments in the specified layer
**/
public int delete(int layer, boolean rightside) {
int cursor = mCursor[layer];
ArrayList<StrSegment> strLayer = mStringLayer[layer];
if (!rightside && cursor > 0) {
deleteStrSegment(layer, cursor-1, cursor-1);
setCursor(layer, cursor - 1);
} else if (rightside && cursor < strLayer.size()) {
deleteStrSegment(layer, cursor, cursor);
setCursor(layer, cursor);
}
return strLayer.size();
}
/**
* Get the string layer.
*
* @param layer Layer
* @return {@link ArrayList} of {@link StrSegment}; {@code null} if error.
**/
public ArrayList<StrSegment> getStringLayer(int layer) {
try {
return mStringLayer[layer];
} catch (Exception ex) {
return null;
}
}
/**
* Get upper the segment which includes the position.
*
* @param layer Layer
* @param pos Position
* @return Index of upper segment
*/
private int included(int layer, int pos) {
if (pos == 0) {
return 0;
}
int uplayer = layer + 1;
int i;
ArrayList<StrSegment> strLayer = mStringLayer[uplayer];
for (i = 0; i < strLayer.size(); i++) {
StrSegment ss = strLayer.get(i);
if (ss.from <= pos && pos <= ss.to) {
break;
}
}
return i;
}
/**
* Set the cursor.
*
* @param layer Layer
* @param pos Position of cursor
* @return New position of cursor
*/
public int setCursor(int layer, int pos) {
if (pos > mStringLayer[layer].size()) {
pos = mStringLayer[layer].size();
}
if (pos < 0) {
pos = 0;
}
if (layer == 0) {
mCursor[0] = pos;
mCursor[1] = included(0, pos);
mCursor[2] = included(1, mCursor[1]);
} else if (layer == 1) {
mCursor[2] = included(1, pos);
mCursor[1] = pos;
mCursor[0] = (pos > 0)? mStringLayer[1].get(pos - 1).to+1 : 0;
} else {
mCursor[2] = pos;
mCursor[1] = (pos > 0)? mStringLayer[2].get(pos - 1).to+1 : 0;
mCursor[0] = (mCursor[1] > 0)? mStringLayer[1].get(mCursor[1] - 1).to+1 : 0;
}
return pos;
}
/**
* Move the cursor.
*
* @param layer Layer
* @param diff Relative position from current cursor position
* @return New position of cursor
**/
public int moveCursor(int layer, int diff) {
int c = mCursor[layer] + diff;
return setCursor(layer, c);
}
/**
* Get the cursor position.
*
* @param layer Layer
* @return cursor Current position of cursor
**/
public int getCursor(int layer) {
return mCursor[layer];
}
/**
* Get the number of segments.
*
* @param layer Layer
* @return Number of segments
**/
public int size(int layer) {
return mStringLayer[layer].size();
}
/**
* Clear all information.
*/
public void clear() {
for (int i = 0; i < MAX_LAYER; i++) {
mStringLayer[i].clear();
mCursor[i] = 0;
}
}
}