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