• 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 @android.ravenwood.annotation.RavenwoodKeepWholeClass
35 public class SpanSet<E> {
36     private final Class<? extends E> classType;
37 
38     int numberOfSpans;
39     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
40     E[] spans;
41     int[] spanStarts;
42     int[] spanEnds;
43     int[] spanFlags;
44 
SpanSet(Class<? extends E> type)45     SpanSet(Class<? extends E> type) {
46         classType = type;
47         numberOfSpans = 0;
48     }
49 
50     @SuppressWarnings("unchecked")
init(Spanned spanned, int start, int limit)51     public void init(Spanned spanned, int start, int limit) {
52         final E[] allSpans = spanned.getSpans(start, limit, classType);
53         final int length = allSpans.length;
54 
55         if (length > 0 && (spans == null || spans.length < length)) {
56             // These arrays may end up being too large because of the discarded empty spans
57             spans = (E[]) Array.newInstance(classType, length);
58             spanStarts = new int[length];
59             spanEnds = new int[length];
60             spanFlags = new int[length];
61         }
62 
63         int prevNumberOfSpans = numberOfSpans;
64         numberOfSpans = 0;
65         for (int i = 0; i < length; i++) {
66             final E span = allSpans[i];
67 
68             final int spanStart = spanned.getSpanStart(span);
69             final int spanEnd = spanned.getSpanEnd(span);
70             if (spanStart == spanEnd) continue;
71 
72             final int spanFlag = spanned.getSpanFlags(span);
73 
74             spans[numberOfSpans] = span;
75             spanStarts[numberOfSpans] = spanStart;
76             spanEnds[numberOfSpans] = spanEnd;
77             spanFlags[numberOfSpans] = spanFlag;
78 
79             numberOfSpans++;
80         }
81 
82         // cleanup extra spans left over from previous init() call
83         if (numberOfSpans < prevNumberOfSpans) {
84             // prevNumberofSpans was > 0, therefore spans != null
85             Arrays.fill(spans, numberOfSpans, prevNumberOfSpans, null);
86         }
87     }
88 
89     /**
90      * Returns true if there are spans intersecting the given interval.
91      * @param end must be strictly greater than start
92      */
hasSpansIntersecting(int start, int end)93     public boolean hasSpansIntersecting(int start, int end) {
94         for (int i = 0; i < numberOfSpans; i++) {
95             // equal test is valid since both intervals are not empty by construction
96             if (spanStarts[i] >= end || spanEnds[i] <= start) continue;
97             return true;
98         }
99         return false;
100     }
101 
102     /**
103      * Similar to {@link Spanned#nextSpanTransition(int, int, Class)}
104      */
getNextTransition(int start, int limit)105     int getNextTransition(int start, int limit) {
106         for (int i = 0; i < numberOfSpans; i++) {
107             final int spanStart = spanStarts[i];
108             final int spanEnd = spanEnds[i];
109             if (spanStart > start && spanStart < limit) limit = spanStart;
110             if (spanEnd > start && spanEnd < limit) limit = spanEnd;
111         }
112         return limit;
113     }
114 
115     /**
116      * Removes all internal references to the spans to avoid memory leaks.
117      */
recycle()118     public void recycle() {
119         if (spans != null) {
120             Arrays.fill(spans, 0, numberOfSpans, null);
121         }
122     }
123 }
124