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.res.Configuration;
216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
226d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.text.BreakIterator;
236d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.util.Locale;
246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov/**
266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * This class contains the implementation of text segment iterators
276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * for accessibility support.
286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Note: Such iterators are needed in the view package since we want
306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * to be able to iterator over content description of any view.
316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide
336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */
346d17a936f73976971135aa1e6248662533343292Svetoslav Ganovpublic final class AccessibilityIterators {
356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    /**
376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     * @hide
386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     */
396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    public static interface TextSegmentIterator {
406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int current);
416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int current);
426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    /**
456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     * @hide
466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     */
476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator {
486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected String mText;
506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private final int[] mSegment = new int[2];
526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void initialize(String text) {
546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mText = text;
556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected int[] getRange(int start, int end) {
586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0 || end < 0 || start ==  end) {
596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mSegment[0] = start;
626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mSegment[1] = end;
636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return mSegment;
646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator
686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            implements ComponentCallbacks {
696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static CharacterTextSegmentIterator sInstance;
706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
71bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        private Locale mLocale;
726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected BreakIterator mImpl;
746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
75bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        public static CharacterTextSegmentIterator getInstance(Locale locale) {
766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
77bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov                sInstance = new CharacterTextSegmentIterator(locale);
786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
82bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        private CharacterTextSegmentIterator(Locale locale) {
83bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov            mLocale = locale;
846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            onLocaleChanged(locale);
856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            ViewRootImpl.addConfigCallback(this);
866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void initialize(String text) {
906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            super.initialize(text);
916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl.setText(text);
926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= textLegth) {
1016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
10339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = offset;
1046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
10539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = 0;
1066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
10739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (!mImpl.isBoundary(start)) {
10839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = mImpl.following(start);
10939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (start == BreakIterator.DONE) {
11039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
11139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                }
1126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int end = mImpl.following(start);
11439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end == BreakIterator.DONE) {
11539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                return null;
11639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            }
1176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
1186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
1226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
1236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
1246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
1276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
12939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = offset;
13039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end > textLegth) {
13139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = textLegth;
1326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
13339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (!mImpl.isBoundary(end)) {
13439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = mImpl.preceding(end);
13539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (end == BreakIterator.DONE) {
13639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
13739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                }
1386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
13939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            final int start = mImpl.preceding(end);
14039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (start == BreakIterator.DONE) {
1416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
1446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void onConfigurationChanged(Configuration newConfig) {
148bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov            Locale locale = newConfig.locale;
149bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov            if (!mLocale.equals(locale)) {
150bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov                mLocale = locale;
1516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                onLocaleChanged(locale);
1526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void onLowMemory() {
1576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            /* ignore */
1586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected void onLocaleChanged(Locale locale) {
1616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl = BreakIterator.getCharacterInstance(locale);
1626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
1646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class WordTextSegmentIterator extends CharacterTextSegmentIterator {
1666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static WordTextSegmentIterator sInstance;
1676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
168bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        public static WordTextSegmentIterator getInstance(Locale locale) {
1696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
170bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov                sInstance = new WordTextSegmentIterator(locale);
1716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
1736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
175bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        private WordTextSegmentIterator(Locale locale) {
176bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov           super(locale);
1776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected void onLocaleChanged(Locale locale) {
1816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl = BreakIterator.getWordInstance(locale);
1826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
1866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
1876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
1886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= mText.length()) {
1916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
19339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = offset;
1946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
19539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = 0;
19639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            }
19739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (!isLetterOrDigit(start) && !isStartBoundary(start)) {
19839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = mImpl.following(start);
19939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (start == BreakIterator.DONE) {
20039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
2016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
2026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
20339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            final int end = mImpl.following(start);
20439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end == BreakIterator.DONE || !isEndBoundary(end)) {
2056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
2126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
2136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
2146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
2176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
21939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = offset;
22039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end > textLegth) {
22139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = textLegth;
2226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
22339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (end > 0 && !isLetterOrDigit(end - 1) && !isEndBoundary(end)) {
22439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = mImpl.preceding(end);
22539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (end == BreakIterator.DONE) {
22639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
2276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
2286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
22939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            final int start = mImpl.preceding(end);
23039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (start == BreakIterator.DONE || !isStartBoundary(start)) {
2316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
23639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isStartBoundary(int index) {
23739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return isLetterOrDigit(index)
23839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == 0 || !isLetterOrDigit(index - 1));
23939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
24039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
24139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isEndBoundary(int index) {
24239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return (index > 0 && isLetterOrDigit(index - 1))
24339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == mText.length() || !isLetterOrDigit(index));
24439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
24539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
2466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private boolean isLetterOrDigit(int index) {
2476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (index >= 0 && index < mText.length()) {
2486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                final int codePoint = mText.codePointAt(index);
2496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return Character.isLetterOrDigit(codePoint);
2506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return false;
2526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
2546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator {
2566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static ParagraphTextSegmentIterator sInstance;
2576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public static ParagraphTextSegmentIterator getInstance() {
2596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
2606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                sInstance = new ParagraphTextSegmentIterator();
2616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
2636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
2676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLength = mText.length();
2686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLength <= 0) {
2696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= textLength) {
2726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
27439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = offset;
2756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
27639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = 0;
2776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
27839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (start < textLength && mText.charAt(start) == '\n'
27939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    && !isStartBoundary(start)) {
2809768832da837e7716555d394bd13fb879a223860alanv                start++;
2819768832da837e7716555d394bd13fb879a223860alanv            }
28239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (start >= textLength) {
28339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                return null;
2846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
28539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = start + 1;
28639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (end < textLength && !isEndBoundary(end)) {
2876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                end++;
2886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
2946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLength = mText.length();
2956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLength <= 0) {
2966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
2996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
3006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
30139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = offset;
30239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end > textLength) {
30339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = textLength;
30439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            }
30539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while(end > 0 && mText.charAt(end - 1) == '\n' && !isEndBoundary(end)) {
30639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end--;
3076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (end <= 0) {
3096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
3106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
31139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = end - 1;
31239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (start > 0 && !isStartBoundary(start)) {
3136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                start--;
3146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
3166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
31739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
31839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isStartBoundary(int index) {
31939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return (mText.charAt(index) != '\n'
32039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == 0 || mText.charAt(index - 1) == '\n'));
32139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
32239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
32339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isEndBoundary(int index) {
32439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return (index > 0 && mText.charAt(index - 1) != '\n'
32539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == mText.length() || mText.charAt(index) == '\n'));
32639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
3276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
3286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov}
329