14ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne/* 24ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Copyright (C) 2012 The Android Open Source Project 34ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * 44ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Licensed under the Apache License, Version 2.0 (the "License"); 54ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * you may not use this file except in compliance with the License. 64ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * You may obtain a copy of the License at 74ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * 84ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * http://www.apache.org/licenses/LICENSE-2.0 94ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * 104ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Unless required by applicable law or agreed to in writing, software 114ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * distributed under the License is distributed on an "AS IS" BASIS, 124ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * See the License for the specific language governing permissions and 144ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * limitations under the License. 154ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne */ 164ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 174ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunnepackage android.text; 184ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 194ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunneimport java.lang.reflect.Array; 20b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4Romain Guyimport java.util.Arrays; 214ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 224ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne/** 234ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * A cached set of spans. Caches the result of {@link Spanned#getSpans(int, int, Class)} and then 244ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * provides faster access to {@link Spanned#nextSpanTransition(int, int, Class)}. 254ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * 264ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Fields are left public for a convenient direct access. 274ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * 284ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Note that empty spans are ignored by this class. 294ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * @hide 304ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne */ 314ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunnepublic class SpanSet<E> { 324ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne private final Class<? extends E> classType; 334ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 344ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne int numberOfSpans; 354ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne E[] spans; 364ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne int[] spanStarts; 374ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne int[] spanEnds; 384ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne int[] spanFlags; 394ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 404ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne SpanSet(Class<? extends E> type) { 414ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne classType = type; 424ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne numberOfSpans = 0; 434ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne } 444ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 454ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne @SuppressWarnings("unchecked") 464ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne public void init(Spanned spanned, int start, int limit) { 474ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne final E[] allSpans = spanned.getSpans(start, limit, classType); 484ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne final int length = allSpans.length; 494ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 504ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne if (length > 0 && (spans == null || spans.length < length)) { 514ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne // These arrays may end up being too large because of the discarded empty spans 524ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne spans = (E[]) Array.newInstance(classType, length); 534ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne spanStarts = new int[length]; 544ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne spanEnds = new int[length]; 554ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne spanFlags = new int[length]; 564ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne } 574ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 58b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4Romain Guy int prevNumberOfSpans = numberOfSpans; 594ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne numberOfSpans = 0; 604ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne for (int i = 0; i < length; i++) { 614ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne final E span = allSpans[i]; 624ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 634ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne final int spanStart = spanned.getSpanStart(span); 644ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne final int spanEnd = spanned.getSpanEnd(span); 654ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne if (spanStart == spanEnd) continue; 664ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 674ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne final int spanFlag = spanned.getSpanFlags(span); 684ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 694ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne spans[numberOfSpans] = span; 704ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne spanStarts[numberOfSpans] = spanStart; 714ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne spanEnds[numberOfSpans] = spanEnd; 724ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne spanFlags[numberOfSpans] = spanFlag; 734ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 744ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne numberOfSpans++; 754ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne } 76b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4Romain Guy 77b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4Romain Guy // cleanup extra spans left over from previous init() call 78b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4Romain Guy if (numberOfSpans < prevNumberOfSpans) { 79b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4Romain Guy // prevNumberofSpans was > 0, therefore spans != null 80b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4Romain Guy Arrays.fill(spans, numberOfSpans, prevNumberOfSpans, null); 81b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4Romain Guy } 824ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne } 834ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 844ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne /** 854ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Returns true if there are spans intersecting the given interval. 864ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * @param end must be strictly greater than start 874ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne */ 884ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne public boolean hasSpansIntersecting(int start, int end) { 894ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne for (int i = 0; i < numberOfSpans; i++) { 904ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne // equal test is valid since both intervals are not empty by construction 914ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne if (spanStarts[i] >= end || spanEnds[i] <= start) continue; 924ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne return true; 934ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne } 944ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne return false; 954ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne } 964ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 974ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne /** 984ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Similar to {@link Spanned#nextSpanTransition(int, int, Class)} 994ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne */ 1004ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne int getNextTransition(int start, int limit) { 1014ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne for (int i = 0; i < numberOfSpans; i++) { 1024ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne final int spanStart = spanStarts[i]; 1034ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne final int spanEnd = spanEnds[i]; 1044ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne if (spanStart > start && spanStart < limit) limit = spanStart; 1054ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne if (spanEnd > start && spanEnd < limit) limit = spanEnd; 1064ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne } 1074ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne return limit; 1084ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne } 1094ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne 1104ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne /** 1114ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne * Removes all internal references to the spans to avoid memory leaks. 1124ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne */ 1134ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne public void recycle() { 114b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4Romain Guy if (spans != null) { 115b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4Romain Guy Arrays.fill(spans, 0, numberOfSpans, null); 1164ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne } 1174ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne } 1184ffb879f4866f7d51070bfc9d10e7a2fdac62d4cGilles Debunne} 119