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 java.lang.reflect.Array;
20 import java.util.Arrays;
21 
22 /**
23  * A cached set of spans. Caches the result of {@link Spanned#getSpans(int, int, Class)} and then
24  * provides faster access to {@link Spanned#nextSpanTransition(int, int, Class)}.
25  *
26  * Fields are left public for a convenient direct access.
27  *
28  * Note that empty spans are ignored by this class.
29  * @hide
30  */
31 public class SpanSet<E> {
32     private final Class<? extends E> classType;
33 
34     int numberOfSpans;
35     E[] spans;
36     int[] spanStarts;
37     int[] spanEnds;
38     int[] spanFlags;
39 
SpanSet(Class<? extends E> type)40     SpanSet(Class<? extends E> type) {
41         classType = type;
42         numberOfSpans = 0;
43     }
44 
45     @SuppressWarnings("unchecked")
init(Spanned spanned, int start, int limit)46     public void init(Spanned spanned, int start, int limit) {
47         final E[] allSpans = spanned.getSpans(start, limit, classType);
48         final int length = allSpans.length;
49 
50         if (length > 0 && (spans == null || spans.length < length)) {
51             // These arrays may end up being too large because of the discarded empty spans
52             spans = (E[]) Array.newInstance(classType, length);
53             spanStarts = new int[length];
54             spanEnds = new int[length];
55             spanFlags = new int[length];
56         }
57 
58         int prevNumberOfSpans = numberOfSpans;
59         numberOfSpans = 0;
60         for (int i = 0; i < length; i++) {
61             final E span = allSpans[i];
62 
63             final int spanStart = spanned.getSpanStart(span);
64             final int spanEnd = spanned.getSpanEnd(span);
65             if (spanStart == spanEnd) continue;
66 
67             final int spanFlag = spanned.getSpanFlags(span);
68 
69             spans[numberOfSpans] = span;
70             spanStarts[numberOfSpans] = spanStart;
71             spanEnds[numberOfSpans] = spanEnd;
72             spanFlags[numberOfSpans] = spanFlag;
73 
74             numberOfSpans++;
75         }
76 
77         // cleanup extra spans left over from previous init() call
78         if (numberOfSpans < prevNumberOfSpans) {
79             // prevNumberofSpans was > 0, therefore spans != null
80             Arrays.fill(spans, numberOfSpans, prevNumberOfSpans, null);
81         }
82     }
83 
84     /**
85      * Returns true if there are spans intersecting the given interval.
86      * @param end must be strictly greater than start
87      */
hasSpansIntersecting(int start, int end)88     public boolean hasSpansIntersecting(int start, int end) {
89         for (int i = 0; i < numberOfSpans; i++) {
90             // equal test is valid since both intervals are not empty by construction
91             if (spanStarts[i] >= end || spanEnds[i] <= start) continue;
92             return true;
93         }
94         return false;
95     }
96 
97     /**
98      * Similar to {@link Spanned#nextSpanTransition(int, int, Class)}
99      */
getNextTransition(int start, int limit)100     int getNextTransition(int start, int limit) {
101         for (int i = 0; i < numberOfSpans; i++) {
102             final int spanStart = spanStarts[i];
103             final int spanEnd = spanEnds[i];
104             if (spanStart > start && spanStart < limit) limit = spanStart;
105             if (spanEnd > start && spanEnd < limit) limit = spanEnd;
106         }
107         return limit;
108     }
109 
110     /**
111      * Removes all internal references to the spans to avoid memory leaks.
112      */
recycle()113     public void recycle() {
114         if (spans != null) {
115             Arrays.fill(spans, 0, numberOfSpans, null);
116         }
117     }
118 }
119