1/*
2 * Copyright (C) 2008,2009  OMRON SOFTWARE Co., Ltd.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package jp.co.omronsoft.openwnn;
18
19import java.util.Iterator;
20import java.util.ArrayList;
21
22import android.util.Log;
23
24/**
25 * The container class of composing string.
26 *
27 * This interface is for the class includes information about the
28 * input string, the converted string and its decoration.
29 * {@link LetterConverter} and {@link WnnEngine} get the input string from it, and
30 * store the converted string into it.
31 *
32 * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
33 */
34public class ComposingText {
35    /**
36     * Text layer 0.
37     * <br>
38     * This text layer holds key strokes.<br>
39     * (ex) Romaji in Japanese.  Parts of Hangul in Korean.
40     */
41    public static final int LAYER0  = 0;
42    /**
43     * Text layer 1.
44     * <br>
45     * This text layer holds the result of the letter converter.<br>
46     * (ex) Hiragana in Japanese. Pinyin in Chinese. Hangul in Korean.
47     */
48    public static final int LAYER1  = 1;
49    /**
50     * Text layer 2.
51     * <br>
52     * This text layer holds the result of the consecutive clause converter.<br>
53     * (ex) the result of Kana-to-Kanji conversion in Japanese,
54     *      Pinyin-to-Kanji conversion in Chinese, Hangul-to-Hanja conversion in Korean language.
55     */
56    public static final int LAYER2  = 2;
57    /** Maximum number of layers */
58    public static final int MAX_LAYER = 3;
59
60    /** Composing text's layer data */
61    protected ArrayList<StrSegment>[] mStringLayer;
62    /** Cursor position */
63    protected int[] mCursor;
64
65    /**
66     * Constructor
67     */
68    public ComposingText() {
69        mStringLayer = new ArrayList[MAX_LAYER];
70        mCursor = new int[MAX_LAYER];
71        for (int i = 0; i < MAX_LAYER; i++) {
72            mStringLayer[i] = new ArrayList<StrSegment>();
73            mCursor[i] = 0;
74        }
75    }
76
77    /**
78     * Output internal information to the log.
79     */
80    public void debugout() {
81        for (int i = 0; i < MAX_LAYER; i++) {
82            Log.d("OpenWnn", "ComposingText["+i+"]");
83            Log.d("OpenWnn", "  cur = " + mCursor[i]);
84            String tmp = "";
85            for (Iterator<StrSegment> it = mStringLayer[i].iterator(); it.hasNext();) {
86                StrSegment ss = it.next();
87                tmp += "(" + ss.string + "," + ss.from + "," + ss.to + ")";
88            }
89            Log.d("OpenWnn", "  str = "+tmp);
90        }
91    }
92
93    /**
94     * Get a {@link StrSegment} at the position specified.
95     *
96     * @param layer     Layer
97     * @param pos       Position (<0 : the tail segment)
98     *
99     * @return          The segment; {@code null} if error occurs.
100     */
101    public StrSegment getStrSegment(int layer, int pos) {
102        try {
103            ArrayList<StrSegment> strLayer = mStringLayer[layer];
104            if (pos < 0) {
105                pos = strLayer.size() - 1;
106            }
107            if (pos >= strLayer.size() || pos < 0) {
108                return null;
109            }
110            return strLayer.get(pos);
111        } catch (Exception ex) {
112            return null;
113        }
114    }
115
116    /**
117     * Convert the range of segments to a string.
118     *
119     * @param layer     Layer
120     * @param from      Convert range from
121     * @param to        Convert range to
122     * @return          The string converted; {@code null} if error occurs.
123     */
124    public String toString(int layer, int from, int to) {
125        try {
126            StringBuffer buf = new StringBuffer();
127            ArrayList<StrSegment> strLayer = mStringLayer[layer];
128
129            for (int i = from; i <= to; i++) {
130                StrSegment ss = strLayer.get(i);
131                buf.append(ss.string);
132            }
133            return buf.toString();
134        } catch (Exception ex) {
135            return null;
136        }
137    }
138
139    /**
140     * Convert segments of the layer to a string.
141     *
142     * @param layer     Layer
143     * @return          The string converted; {@code null} if error occurs.
144     */
145    public String toString(int layer) {
146        return this.toString(layer, 0, mStringLayer[layer].size() - 1);
147    }
148
149    /**
150     * Update the upper layer's data.
151     *
152     * @param layer         The base layer
153     * @param mod_from      Modified from
154     * @param mod_len       Length after modified (# of StrSegments from {@code mod_from})
155     * @param org_len       Length before modified (# of StrSegments from {@code mod_from})
156     */
157    private void modifyUpper(int layer, int mod_from, int mod_len, int org_len) {
158        if (layer >= MAX_LAYER - 1) {
159            /* no layer above */
160            return;
161        }
162
163        int uplayer = layer + 1;
164        ArrayList<StrSegment> strUplayer = mStringLayer[uplayer];
165        if (strUplayer.size() <= 0) {
166            /*
167             * if there is no element on above layer,
168             * add a element includes whole elements of the lower layer.
169             */
170            strUplayer.add(new StrSegment(toString(layer), 0, mStringLayer[layer].size() - 1));
171            modifyUpper(uplayer, 0, 1, 0);
172            return;
173        }
174
175        int mod_to = mod_from + ((mod_len == 0)? 0 : (mod_len - 1));
176        int org_to = mod_from + ((org_len == 0)? 0 : (org_len - 1));
177        StrSegment last = strUplayer.get(strUplayer.size() - 1);
178        if (last.to < mod_from) {
179            /* add at the tail */
180            last.to = mod_to;
181            last.string = toString(layer, last.from, last.to);
182            modifyUpper(uplayer, strUplayer.size()-1, 1, 1);
183            return;
184        }
185
186        int uplayer_mod_from = -1;
187        int uplayer_org_to = -1;
188        for (int i = 0; i < strUplayer.size(); i++) {
189            StrSegment ss = strUplayer.get(i);
190            if (ss.from > mod_from) {
191                if (ss.to <= org_to) {
192                    /* the segment is included */
193                    if (uplayer_mod_from < 0) {
194                        uplayer_mod_from = i;
195                    }
196                    uplayer_org_to = i;
197                } else {
198                    /* included in this segment */
199                    uplayer_org_to = i;
200                    break;
201                }
202            } else {
203                if (org_len == 0 && ss.from == mod_from) {
204                    /* when an element is added */
205                    uplayer_mod_from = i - 1;
206                    uplayer_org_to   = i - 1;
207                    break;
208                } else {
209                    /* start from this segment */
210                    uplayer_mod_from = i;
211                    uplayer_org_to = i;
212                    if (ss.to >= org_to) {
213                        break;
214                    }
215                }
216            }
217        }
218
219        int diff = mod_len - org_len;
220        if (uplayer_mod_from >= 0) {
221            /* update an element */
222            StrSegment ss = strUplayer.get(uplayer_mod_from);
223            int last_to = ss.to;
224            int next = uplayer_mod_from + 1;
225            for (int i = next; i <= uplayer_org_to; i++) {
226                ss = strUplayer.get(next);
227                if (last_to > ss.to) {
228                    last_to = ss.to;
229                }
230                strUplayer.remove(next);
231            }
232            ss.to = (last_to < mod_to)? mod_to : (last_to + diff);
233
234            ss.string = toString(layer, ss.from, ss.to);
235
236            for (int i = next; i < strUplayer.size(); i++) {
237                ss = strUplayer.get(i);
238                ss.from += diff;
239                ss.to   += diff;
240            }
241
242            modifyUpper(uplayer, uplayer_mod_from, 1, uplayer_org_to - uplayer_mod_from + 1);
243        } else {
244            /* add an element at the head */
245            StrSegment ss = new StrSegment(toString(layer, mod_from, mod_to),
246                                           mod_from, mod_to);
247            strUplayer.add(0, ss);
248            for (int i = 1; i < strUplayer.size(); i++) {
249                ss = strUplayer.get(i);
250                ss.from += diff;
251                ss.to   += diff;
252            }
253            modifyUpper(uplayer, 0, 1, 0);
254        }
255
256        return;
257    }
258
259    /**
260     * Insert a {@link StrSegment} at the cursor position.
261     *
262     * @param layer Layer to insert
263     * @param str   String
264     **/
265    public void insertStrSegment(int layer, StrSegment str) {
266        int cursor = mCursor[layer];
267        mStringLayer[layer].add(cursor, str);
268        modifyUpper(layer, cursor, 1, 0);
269        setCursor(layer, cursor + 1);
270    }
271
272    /**
273     * Insert a {@link StrSegment} at the cursor position(without merging to the previous segment).
274     * <p>
275     * @param layer1        Layer to insert
276     * @param layer2        Never merge to the previous segment from {@code layer1} to {@code layer2}.
277     * @param str           String
278     **/
279    public void insertStrSegment(int layer1, int layer2, StrSegment str) {
280        mStringLayer[layer1].add(mCursor[layer1], str);
281        mCursor[layer1]++;
282
283        for (int i = layer1 + 1; i <= layer2; i++) {
284            int pos = mCursor[i-1] - 1;
285            StrSegment tmp = new StrSegment(str.string, pos, pos);
286            ArrayList<StrSegment> strLayer = mStringLayer[i];
287            strLayer.add(mCursor[i], tmp);
288            mCursor[i]++;
289            for (int j = mCursor[i]; j < strLayer.size(); j++) {
290                StrSegment ss = strLayer.get(j);
291                ss.from++;
292                ss.to++;
293            }
294        }
295        int cursor = mCursor[layer2];
296        modifyUpper(layer2, cursor - 1, 1, 0);
297        setCursor(layer2, cursor);
298    }
299
300    /**
301     * Replace segments at the range specified.
302     *
303     * @param layer     Layer
304     * @param str       String segment array to replace
305     * @param from      Replace from
306     * @param to        Replace to
307     **/
308    protected void replaceStrSegment0(int layer, StrSegment[] str, int from, int to) {
309        ArrayList<StrSegment> strLayer = mStringLayer[layer];
310
311        if (from < 0 || from > strLayer.size()) {
312            from = strLayer.size();
313        }
314        if (to < 0 || to > strLayer.size()) {
315            to = strLayer.size();
316        }
317        for (int i = from; i <= to; i++) {
318            strLayer.remove(from);
319        }
320        for (int i = str.length - 1; i >= 0; i--) {
321            strLayer.add(from, str[i]);
322        }
323
324        modifyUpper(layer, from, str.length, to - from + 1);
325    }
326
327    /**
328     * Replace segments at the range specified.
329     *
330     * @param layer     Layer
331     * @param str       String segment array to replace
332     * @param num       Size of string segment array
333     **/
334    public void replaceStrSegment(int layer, StrSegment[] str, int num) {
335        int cursor = mCursor[layer];
336        replaceStrSegment0(layer, str, cursor - num, cursor - 1);
337        setCursor(layer, cursor + str.length - num);
338    }
339
340    /**
341     * Replace the segment at the cursor.
342     *
343     * @param layer     Layer
344     * @param str       String segment to replace
345     **/
346    public void replaceStrSegment(int layer, StrSegment[] str) {
347        int cursor = mCursor[layer];
348        replaceStrSegment0(layer, str, cursor - 1, cursor - 1);
349        setCursor(layer, cursor + str.length - 1);
350    }
351
352    /**
353     * Delete segments.
354     *
355     * @param layer Layer
356     * @param from  Delete from
357     * @param to    Delete to
358     **/
359    public void deleteStrSegment(int layer, int from, int to) {
360        int[] fromL = new int[] {-1, -1, -1};
361        int[] toL   = new int[] {-1, -1, -1};
362
363        ArrayList<StrSegment> strLayer2 = mStringLayer[2];
364        ArrayList<StrSegment> strLayer1 = mStringLayer[1];
365
366        if (layer == 2) {
367            fromL[2] = from;
368            toL[2]   = to;
369            fromL[1] = strLayer2.get(from).from;
370            toL[1]   = strLayer2.get(to).to;
371            fromL[0] = strLayer1.get(fromL[1]).from;
372            toL[0]   = strLayer1.get(toL[1]).to;
373        } else if (layer == 1) {
374            fromL[1] = from;
375            toL[1]   = to;
376            fromL[0] = strLayer1.get(from).from;
377            toL[0]   = strLayer1.get(to).to;
378        } else {
379            fromL[0] = from;
380            toL[0]   = to;
381        }
382
383        int diff = to - from + 1;
384        for (int lv = 0; lv < MAX_LAYER; lv++) {
385            if (fromL[lv] >= 0) {
386                deleteStrSegment0(lv, fromL[lv], toL[lv], diff);
387            } else {
388                int boundary_from = -1;
389                int boundary_to   = -1;
390                ArrayList<StrSegment> strLayer = mStringLayer[lv];
391                for (int i = 0; i < strLayer.size(); i++) {
392                    StrSegment ss = (StrSegment)strLayer.get(i);
393                    if ((ss.from >= fromL[lv-1] && ss.from <= toL[lv-1]) ||
394                        (ss.to >= fromL[lv-1] && ss.to <= toL[lv-1]) ) {
395                        if (fromL[lv] < 0) {
396                            fromL[lv] = i;
397                            boundary_from = ss.from;
398                        }
399                        toL[lv] = i;
400                        boundary_to = ss.to;
401                    } else if (ss.from <= fromL[lv-1] && ss.to >= toL[lv-1]) {
402                        boundary_from = ss.from;
403                        boundary_to   = ss.to;
404                        fromL[lv] = i;
405                        toL[lv] = i;
406                        break;
407                    } else if (ss.from > toL[lv-1]) {
408                        break;
409                    }
410                }
411                if (boundary_from != fromL[lv-1] || boundary_to != toL[lv-1]) {
412                    deleteStrSegment0(lv, fromL[lv] + 1, toL[lv], diff);
413                    boundary_to -= diff;
414                    StrSegment[] tmp = new StrSegment[] {
415                        (new StrSegment(toString(lv-1), boundary_from, boundary_to))
416                    };
417                    replaceStrSegment0(lv, tmp, fromL[lv], fromL[lv]);
418                    return;
419                } else {
420                    deleteStrSegment0(lv, fromL[lv], toL[lv], diff);
421                }
422            }
423            diff = toL[lv] - fromL[lv] + 1;
424        }
425    }
426
427    /**
428     * Delete segments (internal method).
429     *
430     * @param layer     Layer
431     * @param from      Delete from
432     * @param to        Delete to
433     * @param diff      Differential
434     **/
435    private void deleteStrSegment0(int layer, int from, int to, int diff) {
436        ArrayList<StrSegment> strLayer = mStringLayer[layer];
437        if (diff != 0) {
438            for (int i = to + 1; i < strLayer.size(); i++) {
439                StrSegment ss = strLayer.get(i);
440                ss.from -= diff;
441                ss.to   -= diff;
442            }
443        }
444        for (int i = from; i <= to; i++) {
445            strLayer.remove(from);
446        }
447    }
448
449    /**
450     * Delete a segment at the cursor.
451     *
452     * @param layer         Layer
453     * @param rightside     {@code true} if direction is rightward at the cursor, {@code false} if direction is leftward at the cursor
454     * @return              The number of string segments in the specified layer
455     **/
456    public int delete(int layer, boolean rightside) {
457        int cursor = mCursor[layer];
458        ArrayList<StrSegment> strLayer = mStringLayer[layer];
459
460        if (!rightside && cursor > 0) {
461            deleteStrSegment(layer, cursor-1, cursor-1);
462            setCursor(layer, cursor - 1);
463        } else if (rightside && cursor < strLayer.size()) {
464            deleteStrSegment(layer, cursor, cursor);
465            setCursor(layer, cursor);
466        }
467        return strLayer.size();
468    }
469
470    /**
471     * Get the string layer.
472     *
473     * @param layer     Layer
474     * @return          {@link ArrayList} of {@link StrSegment}; {@code null} if error.
475     **/
476    public ArrayList<StrSegment> getStringLayer(int layer) {
477        try {
478            return mStringLayer[layer];
479        } catch (Exception ex) {
480            return null;
481        }
482    }
483
484    /**
485     * Get upper the segment which includes the position.
486     *
487     * @param layer     Layer
488     * @param pos       Position
489     * @return      Index of upper segment
490     */
491    private int included(int layer, int pos) {
492        if (pos == 0) {
493            return 0;
494        }
495        int uplayer = layer + 1;
496        int i;
497        ArrayList<StrSegment> strLayer = mStringLayer[uplayer];
498        for (i = 0; i < strLayer.size(); i++) {
499            StrSegment ss = strLayer.get(i);
500            if (ss.from <= pos && pos <= ss.to) {
501                break;
502            }
503        }
504        return i;
505    }
506
507    /**
508     * Set the cursor.
509     *
510     * @param layer     Layer
511     * @param pos       Position of cursor
512     * @return      New position of cursor
513     */
514    public int setCursor(int layer, int pos) {
515        if (pos > mStringLayer[layer].size()) {
516            pos = mStringLayer[layer].size();
517        }
518        if (pos < 0) {
519            pos = 0;
520        }
521        if (layer == 0) {
522            mCursor[0] = pos;
523            mCursor[1] = included(0, pos);
524            mCursor[2] = included(1, mCursor[1]);
525        } else if (layer == 1) {
526            mCursor[2] = included(1, pos);
527            mCursor[1] = pos;
528            mCursor[0] = (pos > 0)? mStringLayer[1].get(pos - 1).to+1 : 0;
529        } else {
530            mCursor[2] = pos;
531            mCursor[1] = (pos > 0)? mStringLayer[2].get(pos - 1).to+1 : 0;
532            mCursor[0] = (mCursor[1] > 0)? mStringLayer[1].get(mCursor[1] - 1).to+1 : 0;
533        }
534        return pos;
535    }
536
537    /**
538     * Move the cursor.
539     *
540     * @param layer     Layer
541     * @param diff      Relative position from current cursor position
542     * @return      New position of cursor
543     **/
544    public int moveCursor(int layer, int diff) {
545        int c = mCursor[layer] + diff;
546
547        return setCursor(layer, c);
548    }
549
550    /**
551     * Get the cursor position.
552     *
553     * @param layer     Layer
554     * @return cursor   Current position of cursor
555     **/
556    public int getCursor(int layer) {
557        return mCursor[layer];
558    }
559
560    /**
561     * Get the number of segments.
562     *
563     * @param layer     Layer
564     * @return          Number of segments
565     **/
566    public int size(int layer) {
567        return mStringLayer[layer].size();
568    }
569
570    /**
571     * Clear all information.
572     */
573    public void clear() {
574        for (int i = 0; i < MAX_LAYER; i++) {
575            mStringLayer[i].clear();
576            mCursor[i] = 0;
577        }
578    }
579}
580