1aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding/* 2aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * Copyright 2018 The Android Open Source Project 3aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * 4aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * Licensed under the Apache License, Version 2.0 (the "License"); 5aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * you may not use this file except in compliance with the License. 6aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * You may obtain a copy of the License at 7aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * 8aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * http://www.apache.org/licenses/LICENSE-2.0 9aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * 10aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * Unless required by applicable law or agreed to in writing, software 11aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * distributed under the License is distributed on an "AS IS" BASIS, 12aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * See the License for the specific language governing permissions and 14aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * limitations under the License. 15aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding */ 16aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 17aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Hardingpackage androidx.car.widget; 18aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 19aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Hardingimport java.util.ArrayList; 20aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Hardingimport java.util.Arrays; 21aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Hardingimport java.util.Collection; 22aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Hardingimport java.util.Iterator; 23aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Hardingimport java.util.List; 24aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Hardingimport java.util.function.Predicate; 25aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 26aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding/** 27aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * A helper class for building the list of buckets for alpha jump. 28aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding */ 29aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Hardingpublic class AlphaJumpBucketer { 30aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding private static final Character[] DEFAULT_INITIAL_CHARS = { 31aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding '0', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 32aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; 33aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 34aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding private static final String[] PREFIX_WORDS = {"a", "the"}; 35aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 36aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding private final List<Bucket> mBuckets; 37aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 38aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding public AlphaJumpBucketer() { 39aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding mBuckets = new ArrayList<>(); 40aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding for (Character ch : DEFAULT_INITIAL_CHARS) { 41aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding if (ch == '0') { 42aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding mBuckets.add(new Bucket("123", (String s) -> s.matches("^[0-9]"))); 43aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } else { 44aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding String prefix = new String(new char[] {ch}); 45aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding mBuckets.add(new Bucket(prefix, (String s) -> s.startsWith(prefix.toLowerCase()))); 46aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 47aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 48aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 49aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 50aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding public AlphaJumpBucketer(Bucket[] buckets) { 51aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding mBuckets = Arrays.asList(buckets); 52aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 53aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 54aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding /** 55aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * Creates a collection of {@link IAlphaJumpAdapter.Bucket}s from the given list of strings. 56aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding */ 57aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding public Collection<IAlphaJumpAdapter.Bucket> createBuckets(String[] values) { 58aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding return createBuckets(Arrays.asList(values)); 59aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 60aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 61aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding /** 62aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * Creates a collection of {@link IAlphaJumpAdapter.Bucket}s from the given iterable collection 63aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * of strings. 64aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding */ 65aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding public Collection<IAlphaJumpAdapter.Bucket> createBuckets(Iterable<String> values) { 66aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding return createBuckets(values.iterator()); 67aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 68aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 69aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding /** 70aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * Creates the collection of {@link IAlphaJumpAdapter.Bucket}s from the given enumeration of 71aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * values. 72aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding */ 73aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding public Collection<IAlphaJumpAdapter.Bucket> createBuckets(Iterator<String> values) { 74aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding int index = 0; 75aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding while (values.hasNext()) { 76aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding String value = values.next(); 77aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding for (Bucket bucket : mBuckets) { 78aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding if (bucket.matchString(value, index)) { 79aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding break; 80aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 81aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 82aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding index++; 83aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 84aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding ArrayList<IAlphaJumpAdapter.Bucket> buckets = new ArrayList<>(); 85aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding buckets.addAll(mBuckets); 86aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding return buckets; 87aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 88aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 89aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding /** 90aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * "Preprocess" a string so that we remove some common prefixes and so on when performing the 91aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * bucketing. 92aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * 93aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * @param s The string to pre-process. 94aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * @return The input string with whitespace trimmed, and also words like "the", "a" and so on 95aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * removed. 96aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding */ 97aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding private static String preprocess(String s) { 98aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding s = s.trim().toLowerCase(); 99aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 100aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding for (String word : PREFIX_WORDS) { 101aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding if (s.startsWith(word + " ")) { 102aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding s = s.substring(0, word.length() + 1).trim(); 103aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding break; 104aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 105aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 106aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 107aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding return s; 108aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 109aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 110aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding /** 111aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding * A basic implementation of {@link IAlphaJumpAdapter.Bucket}. 112aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding */ 113aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding public static class Bucket implements IAlphaJumpAdapter.Bucket { 114aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding private CharSequence mLabel; 115aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding private int mIndex; 116aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding private Predicate<String> mStringMatcher; 117aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding private boolean mIsEmpty; 118aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 119aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding Bucket(CharSequence label, Predicate<String> stringMatcher) { 120aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding mLabel = label; 121aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding mIndex = -1; 122aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding mIsEmpty = true; 123aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding mStringMatcher = stringMatcher; 124aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 125aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 126aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding boolean matchString(String s, int index) { 127aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding boolean match = mStringMatcher.test(preprocess(s)); 128aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding if (match) { 129aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding if (mIndex < 0) { 130aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding mIndex = index; 131aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 132aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding mIsEmpty = false; 133aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 134aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding return match; 135aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 136aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 137aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding @Override 138aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding public boolean isEmpty() { 139aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding return mIsEmpty; 140aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 141aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 142aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding @Override 143aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding public CharSequence getLabel() { 144aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding return mLabel; 145aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 146aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding 147aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding @Override 148aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding public int getIndex() { 149aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding return mIndex; 150aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 151aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding } 152aa1fd35cceebd3e32536cb11c30d07e28aabc9d1Dean Harding} 153