123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka/*
223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka * Copyright (C) 2016 The Android Open Source Project
323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka *
423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka * Licensed under the Apache License, Version 2.0 (the "License");
523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka * you may not use this file except in compliance with the License.
623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka * You may obtain a copy of the License at
723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka *
823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka *      http://www.apache.org/licenses/LICENSE-2.0
923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka *
1023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka * Unless required by applicable law or agreed to in writing, software
1123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka * distributed under the License is distributed on an "AS IS" BASIS,
1223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka * See the License for the specific language governing permissions and
1423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka * limitations under the License.
1523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka */
1623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
1723a67678e3848e2465518769236139e6cafac2eeSeigo Nonakapackage android.text.method;
1823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
19869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanuimport android.support.test.InstrumentationRegistry;
2068089c81068cc93d3fd42e2f768fc3a2a0d08f3cSiyamed Sinirimport android.support.test.filters.SmallTest;
21bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanuimport android.support.test.runner.AndroidJUnit4;
2223a67678e3848e2465518769236139e6cafac2eeSeigo Nonakaimport android.text.InputType;
23869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanuimport android.util.KeyUtils;
2423a67678e3848e2465518769236139e6cafac2eeSeigo Nonakaimport android.view.KeyEvent;
25869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanuimport android.widget.EditText;
2623a67678e3848e2465518769236139e6cafac2eeSeigo Nonakaimport android.widget.TextView.BufferType;
2723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
28bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanuimport org.junit.Before;
29bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanuimport org.junit.Test;
30bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanuimport org.junit.runner.RunWith;
31bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu
3223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka/**
3323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka * Test forward delete key handling of  {@link android.text.method.BaseKeyListener}.
3423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka *
35132039ae89cfad501cd14fbb3819901151ae1890Seigo Nonaka * Only contains edge cases. For normal cases, see {@see android.text.method.cts.ForwardDeleteTest}.
36132039ae89cfad501cd14fbb3819901151ae1890Seigo Nonaka * TODO: introduce test cases for surrogate pairs and replacement span.
3723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka */
38bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu@SmallTest
39bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu@RunWith(AndroidJUnit4.class)
40869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanupublic class ForwardDeleteTest {
41869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanu    private EditText mTextView;
42869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanu
4323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    private static final BaseKeyListener mKeyListener = new BaseKeyListener() {
4423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        public int getInputType() {
4523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka            return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
4623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        }
4723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    };
4823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
49869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanu    @Before
50869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanu    public void setup() {
51869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanu        mTextView = new EditText(InstrumentationRegistry.getInstrumentation().getContext());
52869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanu    }
53869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanu
5423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    // Sync the state to the TextView and call onKeyDown with KEYCODE_FORWARD_DEL key event.
5523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    // Then update the state to the result of TextView.
5623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    private void forwardDelete(final EditorState state, int modifiers) {
57d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        mTextView.setText(state.mText, BufferType.EDITABLE);
58d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        mTextView.setKeyListener(mKeyListener);
59d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
6023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
61869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanu        final KeyEvent keyEvent = KeyUtils.generateKeyEvent(
62869dd39c5a9c1e55a1d7ab796a05e5e710dff348Andrei Stingaceanu            KeyEvent.KEYCODE_FORWARD_DEL, KeyEvent.ACTION_DOWN, modifiers);
63d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
6423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
6523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.mText = mTextView.getText();
6623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.mSelectionStart = mTextView.getSelectionStart();
6723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.mSelectionEnd = mTextView.getSelectionEnd();
6823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    }
6923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
70bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu    @Test
7123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    public void testCombiningEnclosingKeycaps() {
7223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        EditorState state = new EditorState();
7323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
7423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // multiple COMBINING ENCLOSING KEYCAP
7523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| '1' U+20E3 U+20E3");
7623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
7723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
7823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
7923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Isolated COMBINING ENCLOSING KEYCAP
8023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+20E3");
8123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
8223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
8323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
8423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Isolated multiple COMBINING ENCLOSING KEYCAP
8523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+20E3 U+20E3");
8623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
8723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
8823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    }
8923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
90bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu    @Test
9123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    public void testVariationSelector() {
9223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        EditorState state = new EditorState();
9323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
9423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Isolated variation selectors
9523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+FE0F");
9623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
9723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
9823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
9923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+E0100");
10023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
10123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
10223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
10323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Isolated multiple variation selectors
10423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+FE0F U+FE0F");
10523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
10623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
10723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
10823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+FE0F U+E0100");
10923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
11023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
11123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
11223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+E0100 U+FE0F");
11323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
11423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
11523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
11623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+E0100 U+E0100");
11723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
11823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
11923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
12023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Multiple variation selectors
12123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| '#' U+FE0F U+FE0F");
12223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
12323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
12423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
12523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| '#' U+FE0F U+E0100");
12623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
12723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
12823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
12923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+845B U+E0100 U+FE0F");
13023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
13123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
13223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
13323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+845B U+E0100 U+E0100");
13423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
13523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
13623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    }
13723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
138bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu    @Test
13923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    public void testEmojiZeroWidthJoinerSequence() {
14023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        EditorState state = new EditorState();
14123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
142328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        // U+200D is ZERO WIDTH JOINER.
143328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        state.setByString("| U+1F441 U+200D U+1F5E8");
144328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        forwardDelete(state, 0);
145328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        state.assertEquals("|");
146328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka
147328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        state.setByString("| U+1F468 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468");
148328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        forwardDelete(state, 0);
149328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        state.assertEquals("|");
150328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka
15123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // End with ZERO WIDTH JOINER
15223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F441 U+200D");
15323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
15423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
15523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
15623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Start with ZERO WIDTH JOINER
15723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+200D U+1F5E8");
15823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
15923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F5E8");
16023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
16123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
16223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
16323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Multiple ZERO WIDTH JOINER
16423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F441 U+200D U+200D U+1F5E8");
16523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
16623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F5E8");
16723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
16823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
16923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
17023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Isolated ZERO WIDTH JOINER
17123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+200D");
17223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
17323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
17423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
17523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Isolated multiple ZERO WIDTH JOINER
17623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+200D U+200D");
17723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
17823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
17923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    }
18023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
181bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu    @Test
18223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    public void testFlags() {
18323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        EditorState state = new EditorState();
18423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
18523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Isolated regional indicator symbol
18623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F1FA");
18723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
18823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
18923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
19023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Odd numbered regional indicator symbols
19123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F1FA U+1F1F8 U+1F1FA");
19223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
19323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F1FA");
19423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
19523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
196d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka
197d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        // Incomplete sequence. (no tag_term:U+E007E)
198d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.setByString("| 'a' U+1F3F4 U+E0067 'b'");
199d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
200d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| U+1F3F4 U+E0067 'b'");
201d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
202d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| 'b'");
203d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka
204d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        // No tag_base
205d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.setByString("| 'a' U+E0067 U+E007F 'b'");
206d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
207d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| 'b'");
208d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka
209d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        // Isolated tag chars
210d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.setByString("| 'a' U+E0067 U+E0067 'b'");
211d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
212d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| 'b'");
213d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka
214d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        // Isolated tag base.
215d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.setByString("| 'a' U+1F3F4 U+1F3F4 'b'");
216d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
217d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| U+1F3F4 U+1F3F4 'b'");
218d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
219d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| U+1F3F4 'b'");
220d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
221d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| 'b'");
222d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka
223d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        // Isolated tab term.
224d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.setByString("| 'a' U+E007F U+E007F 'b'");
225d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
226d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| 'b'");
227d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka
228d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        // Immediate tag_term after tag_base
229d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.setByString("| 'a' U+1F3F4 U+E007F U+1F3F4 U+E007F 'b'");
230d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
231d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| U+1F3F4 U+E007F U+1F3F4 U+E007F 'b'");
232d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
233d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| U+1F3F4 U+E007F 'b'");
234d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        forwardDelete(state, 0);
235d5eff80a7721123f2170ff9a983db9f06535d5b4Seigo Nonaka        state.assertEquals("| 'b'");
23623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    }
23723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
238bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu    @Test
23923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    public void testEmojiModifier() {
24023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        EditorState state = new EditorState();
24123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
242328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        // U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2.
243328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        state.setByString("| U+1F466 U+1F3FB");
244328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        forwardDelete(state, 0);
245328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka        state.assertEquals("|");
246328251a1d756463ad5f0dedcdf8a5b613387f8dcSeigo Nonaka
24723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Isolated emoji modifier
24823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F3FB");
24923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
25023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
25123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
25223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Isolated multiple emoji modifier
25323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F3FB U+1F3FB");
25423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
25523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F3FB");
25623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
25723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
25823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
25923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Multiple emoji modifiers
26023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F466 U+1F3FB U+1F3FB");
26123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
26223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F3FB");
26323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
26423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
26523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    }
26623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
267bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu    @Test
26823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    public void testMixedEdgeCases() {
26923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        EditorState state = new EditorState();
27023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
27123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // COMBINING ENCLOSING KEYCAP + variation selector
27223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| '1' U+20E3 U+FE0F");
27323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
27423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
27523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
27623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Variation selector + COMBINING ENCLOSING KEYCAP
27723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+2665 U+FE0F U+20E3");
27823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
27923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
28023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
28123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // COMBINING ENCLOSING KEYCAP + ending with ZERO WIDTH JOINER
28223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| '1' U+20E3 U+200D");
28323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
28423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
28523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
28623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // COMBINING ENCLOSING KEYCAP + ZERO WIDTH JOINER
28723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| '1' U+20E3 U+200D U+1F5E8");
28823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
28923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F5E8 ");
29023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
29123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Start with ZERO WIDTH JOINER + COMBINING ENCLOSING KEYCAP
29223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+200D U+20E3");
29323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
29423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
29523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
29623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // ZERO WIDTH JOINER + COMBINING ENCLOSING KEYCAP
29723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F441 U+200D U+20E3");
29823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
29923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
30023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
30123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // COMBINING ENCLOSING KEYCAP + regional indicator symbol
30223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| '1' U+20E3 U+1F1FA");
30323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
30423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F1FA");
30523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
30623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
30723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
30823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Regional indicator symbol + COMBINING ENCLOSING KEYCAP
30923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F1FA U+20E3");
31023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
31123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
31223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
31323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // COMBINING ENCLOSING KEYCAP + emoji modifier
31423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| '1' U+20E3 U+1F3FB");
31523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
31623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F3FB");
31723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
31823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Emoji modifier + COMBINING ENCLOSING KEYCAP
31923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F466 U+1F3FB U+20E3");
32023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
32123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
32223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
32323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Variation selector + end with ZERO WIDTH JOINER
32423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+2665 U+FE0F U+200D");
32523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
32623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
32723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
32823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Variation selector + ZERO WIDTH JOINER
32923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F469 U+200D U+2764 U+FE0F U+200D U+1F469");
33023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
33123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
33223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
33323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Start with ZERO WIDTH JOINER + variation selector
33423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+200D U+FE0F");
33523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
33623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
33723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
33823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // ZERO WIDTH JOINER + variation selector
33923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F469 U+200D U+FE0F");
34023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
34123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
34223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
34323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Variation selector + regional indicator symbol
34423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+2665 U+FE0F U+1F1FA");
34523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
34623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F1FA");
34723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
34823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
34923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
35023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Regional indicator symbol + variation selector
35123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F1FA U+FE0F");
35223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
35323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
35423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
35523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Variation selector + emoji modifier
35623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+2665 U+FE0F U+1F3FB");
35723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
35823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F3FB");
35923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
36023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Emoji modifier + variation selector
36123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F466 U+1F3FB U+FE0F");
36223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
36323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
36423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
36523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Start with ZERO WIDTH JOINER + regional indicator symbol
36623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+200D U+1F1FA");
36723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
36823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F1FA");
36923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
37023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
37123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
37223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // ZERO WIDTH JOINER + regional indicator symbol
37323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F469 U+200D U+1F1FA");
37423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
3754fbf85b02f3b06882a0ec46d7fbe784d2d8ae1fbRoozbeh Pournader        state.assertEquals("| U+1F1FA");
3764fbf85b02f3b06882a0ec46d7fbe784d2d8ae1fbRoozbeh Pournader        forwardDelete(state, 0);
3771e22227117f6ad86e9efe2a2ca2cca1fc456f07eSeigo Nonaka        state.assertEquals("|");
37823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
37923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Regional indicator symbol + end with ZERO WIDTH JOINER
38023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F1FA U+200D");
38123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
38223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
38323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
38423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Regional indicator symbol + ZERO WIDTH JOINER
38523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F1FA U+200D U+1F469");
38623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
3874fbf85b02f3b06882a0ec46d7fbe784d2d8ae1fbRoozbeh Pournader        state.assertEquals("| U+1F469");
3884fbf85b02f3b06882a0ec46d7fbe784d2d8ae1fbRoozbeh Pournader        forwardDelete(state, 0);
38923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
39023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
39123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Start with ZERO WIDTH JOINER + emoji modifier
39223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+200D U+1F3FB");
39323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
39423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F3FB");
39523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
39623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // ZERO WIDTH JOINER + emoji modifier
39723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F469 U+200D U+1F3FB");
39823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
3991e22227117f6ad86e9efe2a2ca2cca1fc456f07eSeigo Nonaka        state.assertEquals("|");
40023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
40123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Emoji modifier + end with ZERO WIDTH JOINER
40223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F466 U+1F3FB U+200D");
40323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
40423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
40523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
40623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Emoji modifier + ZERO WIDTH JOINER
40723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F466 U+1F3FB U+200D U+1F469");
40823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
4094fbf85b02f3b06882a0ec46d7fbe784d2d8ae1fbRoozbeh Pournader        state.assertEquals("| U+1F469");
4104fbf85b02f3b06882a0ec46d7fbe784d2d8ae1fbRoozbeh Pournader        forwardDelete(state, 0);
41123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
41223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
41323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Regional indicator symbol + emoji modifier
41423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F1FA U+1F3FB");
41523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
41623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F3FB");
41723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
41823a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
41923a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka
42023a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        // Emoji modifier + regional indicator symbol
42123a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.setByString("| U+1F466 U+1F3FB U+1F1FA");
42223a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
42323a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("| U+1F1FA");
42423a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        forwardDelete(state, 0);
42523a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka        state.assertEquals("|");
42623a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka    }
42723a67678e3848e2465518769236139e6cafac2eeSeigo Nonaka}
428