AccessibilityIterators.java revision 6d17a936f73976971135aa1e6248662533343292
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        protected static final int DONE = -1;
516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected String mText;
536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private final int[] mSegment = new int[2];
556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void initialize(String text) {
576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mText = text;
586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected int[] getRange(int start, int end) {
616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0 || end < 0 || start ==  end) {
626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mSegment[0] = start;
656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mSegment[1] = end;
666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return mSegment;
676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator
716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            implements ComponentCallbacks {
726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static CharacterTextSegmentIterator sInstance;
736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private final Context mAppContext;
756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected BreakIterator mImpl;
776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public static CharacterTextSegmentIterator getInstance(Context context) {
796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                sInstance = new CharacterTextSegmentIterator(context);
816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private CharacterTextSegmentIterator(Context context) {
866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mAppContext = context.getApplicationContext();
876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            Locale locale = mAppContext.getResources().getConfiguration().locale;
886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            onLocaleChanged(locale);
896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            ViewRootImpl.addConfigCallback(this);
906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void initialize(String text) {
946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            super.initialize(text);
956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl.setText(text);
966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
1006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
1016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
1026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= textLegth) {
1056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            int start = -1;
1086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset < 0) {
1096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                offset = 0;
1106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                if (mImpl.isBoundary(offset)) {
1116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    start = offset;
1126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
1136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
1156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                start = mImpl.following(offset);
1166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
1186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int end = mImpl.following(start);
1216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
1226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
1266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
1276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
1286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
1316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            int end = -1;
1346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset > mText.length()) {
1356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                offset = mText.length();
1366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                if (mImpl.isBoundary(offset)) {
1376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    end = offset;
1386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
1396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (end < 0) {
1416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                end = mImpl.preceding(offset);
1426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (end < 0) {
1446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int start = mImpl.preceding(end);
1476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
1486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void onConfigurationChanged(Configuration newConfig) {
1526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            Configuration oldConfig = mAppContext.getResources().getConfiguration();
1536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int changed = oldConfig.diff(newConfig);
1546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if ((changed & ActivityInfo.CONFIG_LOCALE) != 0) {
1556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                Locale locale = newConfig.locale;
1566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                onLocaleChanged(locale);
1576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void onLowMemory() {
1626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            /* ignore */
1636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected void onLocaleChanged(Locale locale) {
1666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl = BreakIterator.getCharacterInstance(locale);
1676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
1696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class WordTextSegmentIterator extends CharacterTextSegmentIterator {
1716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static WordTextSegmentIterator sInstance;
1726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public static WordTextSegmentIterator getInstance(Context context) {
1746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
1756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                sInstance = new WordTextSegmentIterator(context);
1766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
1786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private WordTextSegmentIterator(Context context) {
1816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov           super(context);
1826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected void onLocaleChanged(Locale locale) {
1866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl = BreakIterator.getWordInstance(locale);
1876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
1916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
1926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
1936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= mText.length()) {
1966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            int start = -1;
1996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset < 0) {
2006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                offset = 0;
2016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                if (mImpl.isBoundary(offset) && isLetterOrDigit(offset)) {
2026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    start = offset;
2036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
2046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
2066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                while ((offset = mImpl.following(offset)) != DONE) {
2076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    if (isLetterOrDigit(offset)) {
2086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                        start = offset;
2096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                        break;
2106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    }
2116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
2126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
2146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int end = mImpl.following(start);
2176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
2226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
2236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
2246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
2276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            int end = -1;
2306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset > mText.length()) {
2316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                offset = mText.length();
2326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                if (mImpl.isBoundary(offset) && offset > 0 && isLetterOrDigit(offset - 1)) {
2336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    end = offset;
2346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
2356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (end < 0) {
2376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                while ((offset = mImpl.preceding(offset)) != DONE) {
2386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    if (offset > 0 && isLetterOrDigit(offset - 1)) {
2396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                        end = offset;
2406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                        break;
2416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    }
2426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
2436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (end < 0) {
2456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int start = mImpl.preceding(end);
2486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private boolean isLetterOrDigit(int index) {
2526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (index >= 0 && index < mText.length()) {
2536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                final int codePoint = mText.codePointAt(index);
2546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return Character.isLetterOrDigit(codePoint);
2556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return false;
2576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
2596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator {
2616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static ParagraphTextSegmentIterator sInstance;
2626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public static ParagraphTextSegmentIterator getInstance() {
2646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
2656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                sInstance = new ParagraphTextSegmentIterator();
2666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
2686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
2726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLength = mText.length();
2736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLength <= 0) {
2746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= textLength) {
2776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            int start = -1;
2806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset < 0) {
2816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                start = 0;
2826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            } else {
2836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                for (int i = offset + 1; i < textLength; i++) {
2846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    if (mText.charAt(i) == '\n') {
2856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                        start = i;
2866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                        break;
2876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    }
2886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
2896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            while (start < textLength && mText.charAt(start) == '\n') {
2916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                start++;
2926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
2946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            int end = start;
2976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            for (int i = end + 1; i < textLength; i++) {
2986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                end = i;
2996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                if (mText.charAt(i) == '\n') {
3006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    break;
3016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
3026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            while (end < textLength && mText.charAt(end) == '\n') {
3046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                end++;
3056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
3076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
3086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
3096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
3106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
3116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLength = mText.length();
3126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLength <= 0) {
3136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
3146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
3166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
3176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            int end = -1;
3196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset > mText.length()) {
3206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                end = mText.length();
3216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            } else {
3226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                if (offset > 0 && mText.charAt(offset - 1) == '\n') {
3236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    offset--;
3246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
3256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                for (int i = offset - 1; i >= 0; i--) {
3266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    if (i > 0 && mText.charAt(i - 1) == '\n') {
3276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                        end = i;
3286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                        break;
3296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    }
3306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
3316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (end <= 0) {
3336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
3346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            int start = end;
3366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            while (start > 0 && mText.charAt(start - 1) == '\n') {
3376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                start--;
3386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start == 0 && mText.charAt(start) == '\n') {
3406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
3416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            for (int i = start - 1; i >= 0; i--) {
3436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                start = i;
3446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                if (start > 0 && mText.charAt(i - 1) == '\n') {
3456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                    break;
3466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
3476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            start = Math.max(0, start);
3496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
3506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
3516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
3526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov}
353