|  | /* | 
|  | * Copyright (C) 2012 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | package android.text; | 
|  |  | 
|  | import android.compat.annotation.UnsupportedAppUsage; | 
|  | import android.os.Build; | 
|  |  | 
|  | import java.lang.reflect.Array; | 
|  | import java.util.Arrays; | 
|  |  | 
|  | /** | 
|  | * A cached set of spans. Caches the result of {@link Spanned#getSpans(int, int, Class)} and then | 
|  | * provides faster access to {@link Spanned#nextSpanTransition(int, int, Class)}. | 
|  | * | 
|  | * Fields are left public for a convenient direct access. | 
|  | * | 
|  | * Note that empty spans are ignored by this class. | 
|  | * @hide | 
|  | */ | 
|  | public class SpanSet<E> { | 
|  | private final Class<? extends E> classType; | 
|  |  | 
|  | int numberOfSpans; | 
|  | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
|  | E[] spans; | 
|  | int[] spanStarts; | 
|  | int[] spanEnds; | 
|  | int[] spanFlags; | 
|  |  | 
|  | SpanSet(Class<? extends E> type) { | 
|  | classType = type; | 
|  | numberOfSpans = 0; | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("unchecked") | 
|  | public void init(Spanned spanned, int start, int limit) { | 
|  | final E[] allSpans = spanned.getSpans(start, limit, classType); | 
|  | final int length = allSpans.length; | 
|  |  | 
|  | if (length > 0 && (spans == null || spans.length < length)) { | 
|  | // These arrays may end up being too large because of the discarded empty spans | 
|  | spans = (E[]) Array.newInstance(classType, length); | 
|  | spanStarts = new int[length]; | 
|  | spanEnds = new int[length]; | 
|  | spanFlags = new int[length]; | 
|  | } | 
|  |  | 
|  | int prevNumberOfSpans = numberOfSpans; | 
|  | numberOfSpans = 0; | 
|  | for (int i = 0; i < length; i++) { | 
|  | final E span = allSpans[i]; | 
|  |  | 
|  | final int spanStart = spanned.getSpanStart(span); | 
|  | final int spanEnd = spanned.getSpanEnd(span); | 
|  | if (spanStart == spanEnd) continue; | 
|  |  | 
|  | final int spanFlag = spanned.getSpanFlags(span); | 
|  |  | 
|  | spans[numberOfSpans] = span; | 
|  | spanStarts[numberOfSpans] = spanStart; | 
|  | spanEnds[numberOfSpans] = spanEnd; | 
|  | spanFlags[numberOfSpans] = spanFlag; | 
|  |  | 
|  | numberOfSpans++; | 
|  | } | 
|  |  | 
|  | // cleanup extra spans left over from previous init() call | 
|  | if (numberOfSpans < prevNumberOfSpans) { | 
|  | // prevNumberofSpans was > 0, therefore spans != null | 
|  | Arrays.fill(spans, numberOfSpans, prevNumberOfSpans, null); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns true if there are spans intersecting the given interval. | 
|  | * @param end must be strictly greater than start | 
|  | */ | 
|  | public boolean hasSpansIntersecting(int start, int end) { | 
|  | for (int i = 0; i < numberOfSpans; i++) { | 
|  | // equal test is valid since both intervals are not empty by construction | 
|  | if (spanStarts[i] >= end || spanEnds[i] <= start) continue; | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Similar to {@link Spanned#nextSpanTransition(int, int, Class)} | 
|  | */ | 
|  | int getNextTransition(int start, int limit) { | 
|  | for (int i = 0; i < numberOfSpans; i++) { | 
|  | final int spanStart = spanStarts[i]; | 
|  | final int spanEnd = spanEnds[i]; | 
|  | if (spanStart > start && spanStart < limit) limit = spanStart; | 
|  | if (spanEnd > start && spanEnd < limit) limit = spanEnd; | 
|  | } | 
|  | return limit; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Removes all internal references to the spans to avoid memory leaks. | 
|  | */ | 
|  | public void recycle() { | 
|  | if (spans != null) { | 
|  | Arrays.fill(spans, 0, numberOfSpans, null); | 
|  | } | 
|  | } | 
|  | } |