16d17a936f73976971135aa1e6248662533343292Svetoslav Ganov/*
26d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Copyright (C) 2012 The Android Open Source Project
36d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
46d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Licensed under the Apache License, Version 2.0 (the "License");
56d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * you may not use this file except in compliance with the License.
66d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * You may obtain a copy of the License at
76d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
86d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *      http://www.apache.org/licenses/LICENSE-2.0
96d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Unless required by applicable law or agreed to in writing, software
116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * distributed under the License is distributed on an "AS IS" BASIS,
126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * See the License for the specific language governing permissions and
146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * limitations under the License.
156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */
166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
176d17a936f73976971135aa1e6248662533343292Svetoslav Ganovpackage android.view;
186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
196d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport android.content.ComponentCallbacks;
206d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport android.content.Context;
216d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport android.content.pm.ActivityInfo;
226d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport android.content.res.Configuration;
236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
246d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.text.BreakIterator;
256d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.util.Locale;
266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov/**
286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * This class contains the implementation of text segment iterators
296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * for accessibility support.
306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Note: Such iterators are needed in the view package since we want
326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * to be able to iterator over content description of any view.
336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide
356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */
366d17a936f73976971135aa1e6248662533343292Svetoslav Ganovpublic final class AccessibilityIterators {
376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    /**
396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     * @hide
406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     */
416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    public static interface TextSegmentIterator {
426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int current);
436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int current);
446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    /**
476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     * @hide
486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     */
496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator {
506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected String mText;
526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private final int[] mSegment = new int[2];
546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void initialize(String text) {
566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mText = text;
576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected int[] getRange(int start, int end) {
606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0 || end < 0 || start ==  end) {
616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mSegment[0] = start;
646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mSegment[1] = end;
656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return mSegment;
666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator
706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            implements ComponentCallbacks {
716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static CharacterTextSegmentIterator sInstance;
726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
73bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        private Locale mLocale;
746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected BreakIterator mImpl;
766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
77bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        public static CharacterTextSegmentIterator getInstance(Locale locale) {
786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
79bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov                sInstance = new CharacterTextSegmentIterator(locale);
806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
84bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        private CharacterTextSegmentIterator(Locale locale) {
85bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov            mLocale = locale;
866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            onLocaleChanged(locale);
876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            ViewRootImpl.addConfigCallback(this);
886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void initialize(String text) {
926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            super.initialize(text);
936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl.setText(text);
946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
1006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= textLegth) {
1036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
10539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = offset;
1066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
10739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = 0;
1086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
10939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (!mImpl.isBoundary(start)) {
11039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = mImpl.following(start);
11139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (start == BreakIterator.DONE) {
11239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
11339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                }
1146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int end = mImpl.following(start);
11639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end == BreakIterator.DONE) {
11739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                return null;
11839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            }
1196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
1206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
1246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
1256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
1266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
1296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
13139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = offset;
13239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end > textLegth) {
13339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = textLegth;
1346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
13539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (!mImpl.isBoundary(end)) {
13639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = mImpl.preceding(end);
13739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (end == BreakIterator.DONE) {
13839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
13939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                }
1406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
14139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            final int start = mImpl.preceding(end);
14239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (start == BreakIterator.DONE) {
1436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
1466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void onConfigurationChanged(Configuration newConfig) {
150bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov            Locale locale = newConfig.locale;
151bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov            if (!mLocale.equals(locale)) {
152bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov                mLocale = locale;
1536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                onLocaleChanged(locale);
1546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void onLowMemory() {
1596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            /* ignore */
1606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected void onLocaleChanged(Locale locale) {
1636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl = BreakIterator.getCharacterInstance(locale);
1646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
1666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class WordTextSegmentIterator extends CharacterTextSegmentIterator {
1686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static WordTextSegmentIterator sInstance;
1696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
170bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        public static WordTextSegmentIterator getInstance(Locale locale) {
1716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
172bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov                sInstance = new WordTextSegmentIterator(locale);
1736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
1756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
177bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        private WordTextSegmentIterator(Locale locale) {
178bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov           super(locale);
1796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected void onLocaleChanged(Locale locale) {
1836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl = BreakIterator.getWordInstance(locale);
1846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
1886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
1896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
1906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= mText.length()) {
1936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
19539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = offset;
1966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
19739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = 0;
19839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            }
19939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (!isLetterOrDigit(start) && !isStartBoundary(start)) {
20039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = mImpl.following(start);
20139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (start == BreakIterator.DONE) {
20239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
2036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
2046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
20539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            final int end = mImpl.following(start);
20639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end == BreakIterator.DONE || !isEndBoundary(end)) {
2076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
2146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
2156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
2166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
2196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
22139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = offset;
22239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end > textLegth) {
22339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = textLegth;
2246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
22539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (end > 0 && !isLetterOrDigit(end - 1) && !isEndBoundary(end)) {
22639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = mImpl.preceding(end);
22739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (end == BreakIterator.DONE) {
22839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
2296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
2306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
23139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            final int start = mImpl.preceding(end);
23239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (start == BreakIterator.DONE || !isStartBoundary(start)) {
2336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
23839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isStartBoundary(int index) {
23939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return isLetterOrDigit(index)
24039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == 0 || !isLetterOrDigit(index - 1));
24139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
24239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
24339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isEndBoundary(int index) {
24439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return (index > 0 && isLetterOrDigit(index - 1))
24539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == mText.length() || !isLetterOrDigit(index));
24639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
24739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
2486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private boolean isLetterOrDigit(int index) {
2496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (index >= 0 && index < mText.length()) {
2506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                final int codePoint = mText.codePointAt(index);
2516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return Character.isLetterOrDigit(codePoint);
2526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return false;
2546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
2566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator {
2586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static ParagraphTextSegmentIterator sInstance;
2596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public static ParagraphTextSegmentIterator getInstance() {
2616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
2626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                sInstance = new ParagraphTextSegmentIterator();
2636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
2656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
2696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLength = mText.length();
2706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLength <= 0) {
2716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= textLength) {
2746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
27639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = offset;
2776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
27839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = 0;
2796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
28039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (start < textLength && mText.charAt(start) == '\n'
28139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    && !isStartBoundary(start)) {
2829768832da837e7716555d394bd13fb879a223860alanv                start++;
2839768832da837e7716555d394bd13fb879a223860alanv            }
28439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (start >= textLength) {
28539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                return null;
2866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
28739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = start + 1;
28839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (end < textLength && !isEndBoundary(end)) {
2896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                end++;
2906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
2966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLength = mText.length();
2976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLength <= 0) {
2986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
3016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
3026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
30339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = offset;
30439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end > textLength) {
30539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = textLength;
30639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            }
30739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while(end > 0 && mText.charAt(end - 1) == '\n' && !isEndBoundary(end)) {
30839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end--;
3096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (end <= 0) {
3116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
3126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
31339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = end - 1;
31439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (start > 0 && !isStartBoundary(start)) {
3156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                start--;
3166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
3186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
31939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
32039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isStartBoundary(int index) {
32139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return (mText.charAt(index) != '\n'
32239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == 0 || mText.charAt(index - 1) == '\n'));
32339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
32439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
32539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isEndBoundary(int index) {
32639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return (index > 0 && mText.charAt(index - 1) != '\n'
32739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == mText.length() || mText.charAt(index) == '\n'));
32839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
3296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
3306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov}
331