1 /* 2 * Copyright (C) 2024 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 com.android.server.inputmethod; 18 19 import android.annotation.AnyThread; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.util.ArrayMap; 23 import android.view.inputmethod.InputMethodSubtype; 24 25 import java.util.Collection; 26 import java.util.List; 27 28 /** 29 * An on-memory immutable data representation of subtype.xml, which contains so-called additional 30 * {@link InputMethodSubtype}. 31 * 32 * <p>While the data structure could be also used for general purpose map from IME ID to 33 * a list of {@link InputMethodSubtype}, unlike {@link InputMethodMap} this particular data 34 * structure is currently used only around additional {@link InputMethodSubtype}, which is why this 35 * class is (still) called {@code AdditionalSubtypeMap} rather than {@code InputMethodSubtypeMap}. 36 * </p> 37 */ 38 final class AdditionalSubtypeMap { 39 /** 40 * An empty {@link AdditionalSubtypeMap}. 41 */ 42 static final AdditionalSubtypeMap EMPTY_MAP = new AdditionalSubtypeMap(new ArrayMap<>()); 43 44 @NonNull 45 private final ArrayMap<String, List<InputMethodSubtype>> mMap; 46 47 @AnyThread 48 @NonNull createOrEmpty( @onNull ArrayMap<String, List<InputMethodSubtype>> map)49 private static AdditionalSubtypeMap createOrEmpty( 50 @NonNull ArrayMap<String, List<InputMethodSubtype>> map) { 51 return map.isEmpty() ? EMPTY_MAP : new AdditionalSubtypeMap(map); 52 } 53 54 /** 55 * Create a new instance from the given {@link ArrayMap}. 56 * 57 * <p>This method effectively creates a new copy of map.</p> 58 * 59 * @param map An {@link ArrayMap} from which {@link AdditionalSubtypeMap} is to be created. 60 * @return A {@link AdditionalSubtypeMap} that contains a new copy of {@code map}. 61 */ 62 @AnyThread 63 @NonNull of(@onNull ArrayMap<String, List<InputMethodSubtype>> map)64 static AdditionalSubtypeMap of(@NonNull ArrayMap<String, List<InputMethodSubtype>> map) { 65 return createOrEmpty(map); 66 } 67 68 /** 69 * Create a new instance of {@link AdditionalSubtypeMap} from an existing 70 * {@link AdditionalSubtypeMap} by removing {@code key}, or return {@code map} itself if it does 71 * not contain an entry of {@code key}. 72 * 73 * @param key The key to be removed from {@code map}. 74 * @return A new instance of {@link AdditionalSubtypeMap}, which is guaranteed to not contain 75 * {@code key}, or {@code map} itself if it does not contain an entry of {@code key}. 76 */ 77 @AnyThread 78 @NonNull cloneWithRemoveOrSelf(@onNull String key)79 AdditionalSubtypeMap cloneWithRemoveOrSelf(@NonNull String key) { 80 if (isEmpty() || !containsKey(key)) { 81 return this; 82 } 83 final ArrayMap<String, List<InputMethodSubtype>> newMap = new ArrayMap<>(mMap); 84 newMap.remove(key); 85 return createOrEmpty(newMap); 86 } 87 88 /** 89 * Create a new instance of {@link AdditionalSubtypeMap} from an existing 90 * {@link AdditionalSubtypeMap} by removing {@code keys} or return {@code map} itself if it does 91 * not contain any entry for {@code keys}. 92 * 93 * @param keys Keys to be removed from {@code map}. 94 * @return A new instance of {@link AdditionalSubtypeMap}, which is guaranteed to not contain 95 * {@code keys}, or {@code map} itself if it does not contain any entry of {@code keys}. 96 */ 97 @AnyThread 98 @NonNull cloneWithRemoveOrSelf(@onNull Collection<String> keys)99 AdditionalSubtypeMap cloneWithRemoveOrSelf(@NonNull Collection<String> keys) { 100 if (isEmpty()) { 101 return this; 102 } 103 final ArrayMap<String, List<InputMethodSubtype>> newMap = new ArrayMap<>(mMap); 104 return newMap.removeAll(keys) ? createOrEmpty(newMap) : this; 105 } 106 107 /** 108 * Create a new instance of {@link AdditionalSubtypeMap} from an existing 109 * {@link AdditionalSubtypeMap} by putting {@code key} and {@code value}. 110 * 111 * @param key Key to be put into {@code map}. 112 * @param value Value to be put into {@code map}. 113 * @return A new instance of {@link AdditionalSubtypeMap}, which is guaranteed to contain the 114 * pair of {@code key} and {@code value}. 115 */ 116 @AnyThread 117 @NonNull cloneWithPut( @ullable String key, @NonNull List<InputMethodSubtype> value)118 AdditionalSubtypeMap cloneWithPut( 119 @Nullable String key, @NonNull List<InputMethodSubtype> value) { 120 final ArrayMap<String, List<InputMethodSubtype>> newMap = new ArrayMap<>(mMap); 121 newMap.put(key, value); 122 return new AdditionalSubtypeMap(newMap); 123 } 124 AdditionalSubtypeMap(@onNull ArrayMap<String, List<InputMethodSubtype>> map)125 private AdditionalSubtypeMap(@NonNull ArrayMap<String, List<InputMethodSubtype>> map) { 126 mMap = map; 127 } 128 129 @AnyThread 130 @Nullable get(@ullable String key)131 List<InputMethodSubtype> get(@Nullable String key) { 132 return mMap.get(key); 133 } 134 135 @AnyThread containsKey(@ullable String key)136 boolean containsKey(@Nullable String key) { 137 return mMap.containsKey(key); 138 } 139 140 @AnyThread isEmpty()141 boolean isEmpty() { 142 return mMap.isEmpty(); 143 } 144 145 @AnyThread 146 @NonNull keySet()147 Collection<String> keySet() { 148 return mMap.keySet(); 149 } 150 151 @AnyThread size()152 int size() { 153 return mMap.size(); 154 } 155 } 156