1 /* 2 * Copyright (C) 2015 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 package android.util; 17 18 import android.text.TextUtils; 19 import android.util.proto.ProtoOutputStream; 20 21 import java.io.PrintWriter; 22 import java.time.Duration; 23 import java.time.format.DateTimeParseException; 24 25 /** 26 * Parses a list of key=value pairs, separated by some delimiter, and puts the results in 27 * an internal Map. Values can be then queried by key, or if not found, a default value 28 * can be used. 29 * @hide 30 */ 31 @android.ravenwood.annotation.RavenwoodKeepWholeClass 32 public class KeyValueListParser { 33 private final ArrayMap<String, String> mValues = new ArrayMap<>(); 34 private final TextUtils.StringSplitter mSplitter; 35 36 /** 37 * Constructs a new KeyValueListParser. This can be reused for different strings 38 * by calling {@link #setString(String)}. 39 * @param delim The delimiter that separates key=value pairs. 40 */ KeyValueListParser(char delim)41 public KeyValueListParser(char delim) { 42 mSplitter = new TextUtils.SimpleStringSplitter(delim); 43 } 44 45 /** 46 * Resets the parser with a new string to parse. The string is expected to be in the following 47 * format: 48 * <pre>key1=value,key2=value,key3=value</pre> 49 * 50 * where the delimiter is a comma. 51 * 52 * @param str the string to parse. 53 * @throws IllegalArgumentException if the string is malformed. 54 */ setString(String str)55 public void setString(String str) throws IllegalArgumentException { 56 mValues.clear(); 57 if (str != null) { 58 mSplitter.setString(str); 59 for (String pair : mSplitter) { 60 int sep = pair.indexOf('='); 61 if (sep < 0) { 62 mValues.clear(); 63 throw new IllegalArgumentException( 64 "'" + pair + "' in '" + str + "' is not a valid key-value pair"); 65 } 66 mValues.put(pair.substring(0, sep).trim(), pair.substring(sep + 1).trim()); 67 } 68 } 69 } 70 71 /** 72 * Get the value for key as an int. 73 * @param key The key to lookup. 74 * @param def The value to return if the key was not found, or the value was not a long. 75 * @return the int value associated with the key. 76 */ getInt(String key, int def)77 public int getInt(String key, int def) { 78 String value = mValues.get(key); 79 if (value != null) { 80 try { 81 return Integer.parseInt(value); 82 } catch (NumberFormatException e) { 83 // fallthrough 84 } 85 } 86 return def; 87 } 88 89 /** 90 * Get the value for key as a long. 91 * @param key The key to lookup. 92 * @param def The value to return if the key was not found, or the value was not a long. 93 * @return the long value associated with the key. 94 */ getLong(String key, long def)95 public long getLong(String key, long def) { 96 String value = mValues.get(key); 97 if (value != null) { 98 try { 99 return Long.parseLong(value); 100 } catch (NumberFormatException e) { 101 // fallthrough 102 } 103 } 104 return def; 105 } 106 107 /** 108 * Get the value for key as a float. 109 * @param key The key to lookup. 110 * @param def The value to return if the key was not found, or the value was not a float. 111 * @return the float value associated with the key. 112 */ getFloat(String key, float def)113 public float getFloat(String key, float def) { 114 String value = mValues.get(key); 115 if (value != null) { 116 try { 117 return Float.parseFloat(value); 118 } catch (NumberFormatException e) { 119 // fallthrough 120 } 121 } 122 return def; 123 } 124 125 /** 126 * Get the value for key as a string. 127 * @param key The key to lookup. 128 * @param def The value to return if the key was not found. 129 * @return the string value associated with the key. 130 */ getString(String key, String def)131 public String getString(String key, String def) { 132 String value = mValues.get(key); 133 if (value != null) { 134 return value; 135 } 136 return def; 137 } 138 139 /** 140 * Get the value for key as a boolean. 141 * @param key The key to lookup. 142 * @param def The value to return if the key was not found. 143 * @return the string value associated with the key. 144 */ getBoolean(String key, boolean def)145 public boolean getBoolean(String key, boolean def) { 146 String value = mValues.get(key); 147 if (value != null) { 148 try { 149 return Boolean.parseBoolean(value); 150 } catch (NumberFormatException e) { 151 // fallthrough 152 } 153 } 154 return def; 155 } 156 157 /** 158 * Get the value for key as an integer array. 159 * 160 * The value should be encoded as "0:1:2:3:4" 161 * 162 * @param key The key to lookup. 163 * @param def The value to return if the key was not found. 164 * @return the int[] value associated with the key. 165 */ getIntArray(String key, int[] def)166 public int[] getIntArray(String key, int[] def) { 167 String value = mValues.get(key); 168 if (value != null) { 169 try { 170 String[] parts = value.split(":"); 171 if (parts.length > 0) { 172 int[] ret = new int[parts.length]; 173 for (int i = 0; i < parts.length; i++) { 174 ret[i] = Integer.parseInt(parts[i]); 175 } 176 return ret; 177 } 178 } catch (NumberFormatException e) { 179 // fallthrough 180 } 181 } 182 return def; 183 } 184 185 /** 186 * @return the number of keys. 187 */ size()188 public int size() { 189 return mValues.size(); 190 } 191 192 /** 193 * @return the key at {@code index}. Use with {@link #size()} to enumerate all key-value pairs. 194 */ keyAt(int index)195 public String keyAt(int index) { 196 return mValues.keyAt(index); 197 } 198 199 /** 200 * {@hide} 201 * Parse a duration in millis based on java.time.Duration or just a number (millis) 202 */ getDurationMillis(String key, long def)203 public long getDurationMillis(String key, long def) { 204 String value = mValues.get(key); 205 if (value != null) { 206 try { 207 if (value.startsWith("P") || value.startsWith("p")) { 208 return Duration.parse(value).toMillis(); 209 } else { 210 return Long.parseLong(value); 211 } 212 } catch (NumberFormatException | DateTimeParseException e) { 213 // fallthrough 214 } 215 } 216 return def; 217 } 218 219 /** Represents an integer config value. */ 220 public static class IntValue { 221 private final String mKey; 222 private final int mDefaultValue; 223 private int mValue; 224 225 /** Constructor, initialize with a config key and a default value. */ IntValue(String key, int defaultValue)226 public IntValue(String key, int defaultValue) { 227 mKey = key; 228 mDefaultValue = defaultValue; 229 mValue = mDefaultValue; 230 } 231 232 /** Read a value from {@link KeyValueListParser} */ parse(KeyValueListParser parser)233 public void parse(KeyValueListParser parser) { 234 mValue = parser.getInt(mKey, mDefaultValue); 235 } 236 237 /** Return the config key. */ getKey()238 public String getKey() { 239 return mKey; 240 } 241 242 /** Return the default value. */ getDefaultValue()243 public int getDefaultValue() { 244 return mDefaultValue; 245 } 246 247 /** Return the actual config value. */ getValue()248 public int getValue() { 249 return mValue; 250 } 251 252 /** Overwrites with a value. */ setValue(int value)253 public void setValue(int value) { 254 mValue = value; 255 } 256 257 /** Used for dumpsys */ dump(PrintWriter pw, String prefix)258 public void dump(PrintWriter pw, String prefix) { 259 pw.print(prefix); 260 pw.print(mKey); 261 pw.print("="); 262 pw.print(mValue); 263 pw.println(); 264 } 265 266 /** Used for proto dumpsys */ dumpProto(ProtoOutputStream proto, long tag)267 public void dumpProto(ProtoOutputStream proto, long tag) { 268 proto.write(tag, mValue); 269 } 270 } 271 272 /** Represents an long config value. */ 273 public static class LongValue { 274 private final String mKey; 275 private final long mDefaultValue; 276 private long mValue; 277 278 /** Constructor, initialize with a config key and a default value. */ LongValue(String key, long defaultValue)279 public LongValue(String key, long defaultValue) { 280 mKey = key; 281 mDefaultValue = defaultValue; 282 mValue = mDefaultValue; 283 } 284 285 /** Read a value from {@link KeyValueListParser} */ parse(KeyValueListParser parser)286 public void parse(KeyValueListParser parser) { 287 mValue = parser.getLong(mKey, mDefaultValue); 288 } 289 290 /** Return the config key. */ getKey()291 public String getKey() { 292 return mKey; 293 } 294 295 /** Return the default value. */ getDefaultValue()296 public long getDefaultValue() { 297 return mDefaultValue; 298 } 299 300 /** Return the actual config value. */ getValue()301 public long getValue() { 302 return mValue; 303 } 304 305 /** Overwrites with a value. */ setValue(long value)306 public void setValue(long value) { 307 mValue = value; 308 } 309 310 /** Used for dumpsys */ dump(PrintWriter pw, String prefix)311 public void dump(PrintWriter pw, String prefix) { 312 pw.print(prefix); 313 pw.print(mKey); 314 pw.print("="); 315 pw.print(mValue); 316 pw.println(); 317 } 318 319 /** Used for proto dumpsys */ dumpProto(ProtoOutputStream proto, long tag)320 public void dumpProto(ProtoOutputStream proto, long tag) { 321 proto.write(tag, mValue); 322 } 323 } 324 325 /** Represents an string config value. */ 326 public static class StringValue { 327 private final String mKey; 328 private final String mDefaultValue; 329 private String mValue; 330 331 /** Constructor, initialize with a config key and a default value. */ StringValue(String key, String defaultValue)332 public StringValue(String key, String defaultValue) { 333 mKey = key; 334 mDefaultValue = defaultValue; 335 mValue = mDefaultValue; 336 } 337 338 /** Read a value from {@link KeyValueListParser} */ parse(KeyValueListParser parser)339 public void parse(KeyValueListParser parser) { 340 mValue = parser.getString(mKey, mDefaultValue); 341 } 342 343 /** Return the config key. */ getKey()344 public String getKey() { 345 return mKey; 346 } 347 348 /** Return the default value. */ getDefaultValue()349 public String getDefaultValue() { 350 return mDefaultValue; 351 } 352 353 /** Return the actual config value. */ getValue()354 public String getValue() { 355 return mValue; 356 } 357 358 /** Overwrites with a value. */ setValue(String value)359 public void setValue(String value) { 360 mValue = value; 361 } 362 363 /** Used for dumpsys */ dump(PrintWriter pw, String prefix)364 public void dump(PrintWriter pw, String prefix) { 365 pw.print(prefix); 366 pw.print(mKey); 367 pw.print("="); 368 pw.print(mValue); 369 pw.println(); 370 } 371 372 /** Used for proto dumpsys */ dumpProto(ProtoOutputStream proto, long tag)373 public void dumpProto(ProtoOutputStream proto, long tag) { 374 proto.write(tag, mValue); 375 } 376 } 377 378 /** Represents an float config value. */ 379 public static class FloatValue { 380 private final String mKey; 381 private final float mDefaultValue; 382 private float mValue; 383 384 /** Constructor, initialize with a config key and a default value. */ FloatValue(String key, float defaultValue)385 public FloatValue(String key, float defaultValue) { 386 mKey = key; 387 mDefaultValue = defaultValue; 388 mValue = mDefaultValue; 389 } 390 391 /** Read a value from {@link KeyValueListParser} */ parse(KeyValueListParser parser)392 public void parse(KeyValueListParser parser) { 393 mValue = parser.getFloat(mKey, mDefaultValue); 394 } 395 396 /** Return the config key. */ getKey()397 public String getKey() { 398 return mKey; 399 } 400 401 /** Return the default value. */ getDefaultValue()402 public float getDefaultValue() { 403 return mDefaultValue; 404 } 405 406 /** Return the actual config value. */ getValue()407 public float getValue() { 408 return mValue; 409 } 410 411 /** Overwrites with a value. */ setValue(float value)412 public void setValue(float value) { 413 mValue = value; 414 } 415 416 /** Used for dumpsys */ dump(PrintWriter pw, String prefix)417 public void dump(PrintWriter pw, String prefix) { 418 pw.print(prefix); 419 pw.print(mKey); 420 pw.print("="); 421 pw.print(mValue); 422 pw.println(); 423 } 424 425 /** Used for proto dumpsys */ dumpProto(ProtoOutputStream proto, long tag)426 public void dumpProto(ProtoOutputStream proto, long tag) { 427 proto.write(tag, mValue); 428 } 429 } 430 } 431