14ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne/*
24ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Copyright (C) 2012 The Android Open Source Project
34ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne *
44ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Licensed under the Apache License, Version 2.0 (the "License");
54ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * you may not use this file except in compliance with the License.
64ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * You may obtain a copy of the License at
74ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne *
84ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne *      http://www.apache.org/licenses/LICENSE-2.0
94ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne *
104ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Unless required by applicable law or agreed to in writing, software
114ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * distributed under the License is distributed on an "AS IS" BASIS,
124ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * See the License for the specific language governing permissions and
144ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * limitations under the License.
154ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne */
164ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
174ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunnepackage android.text;
184ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
194ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunneimport java.lang.reflect.Array;
204ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
214ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne/**
224ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * A cached set of spans. Caches the result of {@link Spanned#getSpans(int, int, Class)} and then
234ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * provides faster access to {@link Spanned#nextSpanTransition(int, int, Class)}.
244ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne *
254ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Fields are left public for a convenient direct access.
264ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne *
274ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Note that empty spans are ignored by this class.
284ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * @hide
294ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne */
304ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunnepublic class SpanSet<E> {
314ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    private final Class<? extends E> classType;
324ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
334ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    int numberOfSpans;
344ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    E[] spans;
354ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    int[] spanStarts;
364ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    int[] spanEnds;
374ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    int[] spanFlags;
384ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
394ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    SpanSet(Class<? extends E> type) {
404ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        classType = type;
414ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        numberOfSpans = 0;
424ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    }
434ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
444ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    @SuppressWarnings("unchecked")
454ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    public void init(Spanned spanned, int start, int limit) {
464ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        final E[] allSpans = spanned.getSpans(start, limit, classType);
474ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        final int length = allSpans.length;
484ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
494ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        if (length > 0 && (spans == null || spans.length < length)) {
504ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            // These arrays may end up being too large because of the discarded empty spans
514ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            spans = (E[]) Array.newInstance(classType, length);
524ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            spanStarts = new int[length];
534ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            spanEnds = new int[length];
544ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            spanFlags = new int[length];
554ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        }
564ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
574ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        numberOfSpans = 0;
584ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        for (int i = 0; i < length; i++) {
594ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            final E span = allSpans[i];
604ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
614ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            final int spanStart = spanned.getSpanStart(span);
624ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            final int spanEnd = spanned.getSpanEnd(span);
634ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            if (spanStart == spanEnd) continue;
644ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
654ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            final int spanFlag = spanned.getSpanFlags(span);
664ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
674ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            spans[numberOfSpans] = span;
684ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            spanStarts[numberOfSpans] = spanStart;
694ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            spanEnds[numberOfSpans] = spanEnd;
704ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            spanFlags[numberOfSpans] = spanFlag;
714ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
724ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            numberOfSpans++;
734ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        }
744ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    }
754ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
764ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    /**
774ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne     * Returns true if there are spans intersecting the given interval.
784ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne     * @param end must be strictly greater than start
794ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne     */
804ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    public boolean hasSpansIntersecting(int start, int end) {
814ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        for (int i = 0; i < numberOfSpans; i++) {
824ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            // equal test is valid since both intervals are not empty by construction
834ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            if (spanStarts[i] >= end || spanEnds[i] <= start) continue;
844ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            return true;
854ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        }
864ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        return false;
874ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    }
884ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
894ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    /**
904ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne     * Similar to {@link Spanned#nextSpanTransition(int, int, Class)}
914ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne     */
924ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    int getNextTransition(int start, int limit) {
934ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        for (int i = 0; i < numberOfSpans; i++) {
944ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            final int spanStart = spanStarts[i];
954ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            final int spanEnd = spanEnds[i];
964ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            if (spanStart > start && spanStart < limit) limit = spanStart;
974ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            if (spanEnd > start && spanEnd < limit) limit = spanEnd;
984ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        }
994ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        return limit;
1004ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    }
1014ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne
1024ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    /**
1034ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne     * Removes all internal references to the spans to avoid memory leaks.
1044ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne     */
1054ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    public void recycle() {
1064ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        // The spans array is guaranteed to be not null when numberOfSpans is > 0
1074ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        for (int i = 0; i < numberOfSpans; i++) {
1084ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne            spans[i] = null; // prevent a leak: no reference kept when TextLine is recycled
1094ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne        }
1104ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne    }
1114ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne}
112