• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.text;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.Build;
21 
22 import java.lang.reflect.Array;
23 import java.util.Arrays;
24 
25 /**
26  * A cached set of spans. Caches the result of {@link Spanned#getSpans(int, int, Class)} and then
27  * provides faster access to {@link Spanned#nextSpanTransition(int, int, Class)}.
28  *
29  * Fields are left public for a convenient direct access.
30  *
31  * Note that empty spans are ignored by this class.
32  * @hide
33  */
34 public class SpanSet<E> {
35     private final Class<? extends E> classType;
36 
37     int numberOfSpans;
38     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
39     E[] spans;
40     int[] spanStarts;
41     int[] spanEnds;
42     int[] spanFlags;
43 
SpanSet(Class<? extends E> type)44     SpanSet(Class<? extends E> type) {
45         classType = type;
46         numberOfSpans = 0;
47     }
48 
49     @SuppressWarnings("unchecked")
init(Spanned spanned, int start, int limit)50     public void init(Spanned spanned, int start, int limit) {
51         final E[] allSpans = spanned.getSpans(start, limit, classType);
52         final int length = allSpans.length;
53 
54         if (length > 0 && (spans == null || spans.length < length)) {
55             // These arrays may end up being too large because of the discarded empty spans
56             spans = (E[]) Array.newInstance(classType, length);
57             spanStarts = new int[length];
58             spanEnds = new int[length];
59             spanFlags = new int[length];
60         }
61 
62         int prevNumberOfSpans = numberOfSpans;
63         numberOfSpans = 0;
64         for (int i = 0; i < length; i++) {
65             final E span = allSpans[i];
66 
67             final int spanStart = spanned.getSpanStart(span);
68             final int spanEnd = spanned.getSpanEnd(span);
69             if (spanStart == spanEnd) continue;
70 
71             final int spanFlag = spanned.getSpanFlags(span);
72 
73             spans[numberOfSpans] = span;
74             spanStarts[numberOfSpans] = spanStart;
75             spanEnds[numberOfSpans] = spanEnd;
76             spanFlags[numberOfSpans] = spanFlag;
77 
78             numberOfSpans++;
79         }
80 
81         // cleanup extra spans left over from previous init() call
82         if (numberOfSpans < prevNumberOfSpans) {
83             // prevNumberofSpans was > 0, therefore spans != null
84             Arrays.fill(spans, numberOfSpans, prevNumberOfSpans, null);
85         }
86     }
87 
88     /**
89      * Returns true if there are spans intersecting the given interval.
90      * @param end must be strictly greater than start
91      */
hasSpansIntersecting(int start, int end)92     public boolean hasSpansIntersecting(int start, int end) {
93         for (int i = 0; i < numberOfSpans; i++) {
94             // equal test is valid since both intervals are not empty by construction
95             if (spanStarts[i] >= end || spanEnds[i] <= start) continue;
96             return true;
97         }
98         return false;
99     }
100 
101     /**
102      * Similar to {@link Spanned#nextSpanTransition(int, int, Class)}
103      */
getNextTransition(int start, int limit)104     int getNextTransition(int start, int limit) {
105         for (int i = 0; i < numberOfSpans; i++) {
106             final int spanStart = spanStarts[i];
107             final int spanEnd = spanEnds[i];
108             if (spanStart > start && spanStart < limit) limit = spanStart;
109             if (spanEnd > start && spanEnd < limit) limit = spanEnd;
110         }
111         return limit;
112     }
113 
114     /**
115      * Removes all internal references to the spans to avoid memory leaks.
116      */
recycle()117     public void recycle() {
118         if (spans != null) {
119             Arrays.fill(spans, 0, numberOfSpans, null);
120         }
121     }
122 }
123