Home | History | Annotate | Download | only in text
      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 
     40     SpanSet(Class<? extends E> type) {
     41         classType = type;
     42         numberOfSpans = 0;
     43     }
     44 
     45     @SuppressWarnings("unchecked")
     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      */
     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      */
    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      */
    113     public void recycle() {
    114         if (spans != null) {
    115             Arrays.fill(spans, 0, numberOfSpans, null);
    116         }
    117     }
    118 }
    119