content: /*
   * Copyright (C) 2015 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package android.util;
  
  import android.text.TextUtils;
  import android.util.proto.ProtoOutputStream;
  
  import java.io.PrintWriter;
  import java.time.Duration;
  import java.time.format.DateTimeParseException;
  
  /**
   * Parses a list of key=value pairs, separated by some delimiter, and puts the results in
   * an internal Map. Values can be then queried by key, or if not found, a default value
   * can be used.
   * @hide
   */
  public class KeyValueListParser {
      private final ArrayMap<String, String> mValues = new ArrayMap<>();
      private final TextUtils.StringSplitter mSplitter;
  
      /**
       * Constructs a new KeyValueListParser. This can be reused for different strings
       * by calling {@link #setString(String)}.
       * @param delim The delimiter that separates key=value pairs.
       */
      public KeyValueListParser(char delim) {
          mSplitter = new TextUtils.SimpleStringSplitter(delim);
      }
  
      /**
       * Resets the parser with a new string to parse. The string is expected to be in the following
       * format:
       * <pre>key1=value,key2=value,key3=value</pre>
       *
       * where the delimiter is a comma.
       *
       * @param str the string to parse.
       * @throws IllegalArgumentException if the string is malformed.
       */
      public void setString(String str) throws IllegalArgumentException {
          mValues.clear();
          if (str != null) {
              mSplitter.setString(str);
              for (String pair : mSplitter) {
                  int sep = pair.indexOf('=');
                  if (sep < 0) {
                      mValues.clear();
                      throw new IllegalArgumentException(
                              "'" + pair + "' in '" + str + "' is not a valid key-value pair");
                  }
                  mValues.put(pair.substring(0, sep).trim(), pair.substring(sep + 1).trim());
              }
          }
      }
  
      /**
       * Get the value for key as an int.
       * @param key The key to lookup.
       * @param def The value to return if the key was not found, or the value was not a long.
       * @return the int value associated with the key.
       */
      public int getInt(String key, int def) {
          String value = mValues.get(key);
          if (value != null) {
              try {
                  return Integer.parseInt(value);
              } catch (NumberFormatException e) {
                  // fallthrough
              }
          }
          return def;
      }
  
      /**
       * Get the value for key as a long.
       * @param key The key to lookup.
       * @param def The value to return if the key was not found, or the value was not a long.
       * @return the long value associated with the key.
       */
      public long getLong(String key, long def) {
          String value = mValues.get(key);
          if (value != null) {
              try {
                  return Long.parseLong(value);
              } catch (NumberFormatException e) {
                  // fallthrough
              }
          }
          return def;
      }
  
      /**
       * Get the value for key as a float.
       * @param key The key to lookup.
       * @param def The value to return if the key was not found, or the value was not a float.
       * @return the float value associated with the key.
       */
      public float getFloat(String key, float def) {
          String value = mValues.get(key);
          if (value != null) {
              try {
                  return Float.parseFloat(value);
              } catch (NumberFormatException e) {
                  // fallthrough
              }
          }
          return def;
      }
  
      /**
       * Get the value for key as a string.
       * @param key The key to lookup.
       * @param def The value to return if the key was not found.
       * @return the string value associated with the key.
       */
      public String getString(String key, String def) {
          String value = mValues.get(key);
          if (value != null) {
              return value;
          }
          return def;
      }
  
      /**
       * Get the value for key as a boolean.
       * @param key The key to lookup.
       * @param def The value to return if the key was not found.
       * @return the string value associated with the key.
       */
      public boolean getBoolean(String key, boolean def) {
          String value = mValues.get(key);
          if (value != null) {
              try {
                  return Boolean.parseBoolean(value);
              } catch (NumberFormatException e) {
                  // fallthrough
              }
          }
          return def;
      }
  
      /**
       * Get the value for key as an integer array.
       *
       * The value should be encoded as "0:1:2:3:4"
       *
       * @param key The key to lookup.
       * @param def The value to return if the key was not found.
       * @return the int[] value associated with the key.
       */
      public int[] getIntArray(String key, int[] def) {
          String value = mValues.get(key);
          if (value != null) {
              try {
                  String[] parts = value.split(":");
                  if (parts.length > 0) {
                      int[] ret = new int[parts.length];
                      for (int i = 0; i < parts.length; i++) {
                          ret[i] = Integer.parseInt(parts[i]);
                      }
                      return ret;
                  }
              } catch (NumberFormatException e) {
                  // fallthrough
              }
          }
          return def;
      }
  
      /**
       * @return the number of keys.
       */
      public int size() {
          return mValues.size();
      }
  
      /**
       * @return the key at {@code index}. Use with {@link #size()} to enumerate all key-value pairs.
       */
      public String keyAt(int index) {
          return mValues.keyAt(index);
      }
  
      /**
       * {@hide}
       * Parse a duration in millis based on java.time.Duration or just a number (millis)
       */
      public long getDurationMillis(String key, long def) {
          String value = mValues.get(key);
          if (value != null) {
              try {
                  if (value.startsWith("P") || value.startsWith("p")) {
                      return Duration.parse(value).toMillis();
                  } else {
                      return Long.parseLong(value);
                  }
              } catch (NumberFormatException | DateTimeParseException e) {
                  // fallthrough
              }
          }
          return def;
      }
  
      /** Represents an integer config value. */
      public static class IntValue {
          private final String mKey;
          private final int mDefaultValue;
          private int mValue;
  
          /** Constructor, initialize with a config key and a default value. */
          public IntValue(String key, int defaultValue) {
              mKey = key;
              mDefaultValue = defaultValue;
              mValue = mDefaultValue;
          }
  
          /** Read a value from {@link KeyValueListParser} */
          public void parse(KeyValueListParser parser) {
              mValue = parser.getInt(mKey, mDefaultValue);
          }
  
          /** Return the config key. */
          public String getKey() {
              return mKey;
          }
  
          /** Return the default value. */
          public int getDefaultValue() {
              return mDefaultValue;
          }
  
          /** Return the actual config value. */
          public int getValue() {
              return mValue;
          }
  
          /** Overwrites with a value. */
          public void setValue(int value) {
              mValue = value;
          }
  
          /** Used for dumpsys */
          public void dump(PrintWriter pw, String prefix) {
              pw.print(prefix);
              pw.print(mKey);
              pw.print("=");
              pw.print(mValue);
              pw.println();
          }
  
          /** Used for proto dumpsys */
          public void dumpProto(ProtoOutputStream proto, long tag) {
              proto.write(tag, mValue);
          }
      }
  
      /** Represents an long config value. */
      public static class LongValue {
          private final String mKey;
          private final long mDefaultValue;
          private long mValue;
  
          /** Constructor, initialize with a config key and a default value. */
          public LongValue(String key, long defaultValue) {
              mKey = key;
              mDefaultValue = defaultValue;
              mValue = mDefaultValue;
          }
  
          /** Read a value from {@link KeyValueListParser} */
          public void parse(KeyValueListParser parser) {
              mValue = parser.getLong(mKey, mDefaultValue);
          }
  
          /** Return the config key. */
          public String getKey() {
              return mKey;
          }
  
          /** Return the default value. */
          public long getDefaultValue() {
              return mDefaultValue;
          }
  
          /** Return the actual config value. */
          public long getValue() {
              return mValue;
          }
  
          /** Overwrites with a value. */
          public void setValue(long value) {
              mValue = value;
          }
  
          /** Used for dumpsys */
          public void dump(PrintWriter pw, String prefix) {
              pw.print(prefix);
              pw.print(mKey);
              pw.print("=");
              pw.print(mValue);
              pw.println();
          }
  
          /** Used for proto dumpsys */
          public void dumpProto(ProtoOutputStream proto, long tag) {
              proto.write(tag, mValue);
          }
      }
  
      /** Represents an string config value. */
      public static class StringValue {
          private final String mKey;
          private final String mDefaultValue;
          private String mValue;
  
          /** Constructor, initialize with a config key and a default value. */
          public StringValue(String key, String defaultValue) {
              mKey = key;
              mDefaultValue = defaultValue;
              mValue = mDefaultValue;
          }
  
          /** Read a value from {@link KeyValueListParser} */
          public void parse(KeyValueListParser parser) {
              mValue = parser.getString(mKey, mDefaultValue);
          }
  
          /** Return the config key. */
          public String getKey() {
              return mKey;
          }
  
          /** Return the default value. */
          public String getDefaultValue() {
              return mDefaultValue;
          }
  
          /** Return the actual config value. */
          public String getValue() {
              return mValue;
          }
  
          /** Overwrites with a value. */
          public void setValue(String value) {
              mValue = value;
          }
  
          /** Used for dumpsys */
          public void dump(PrintWriter pw, String prefix) {
              pw.print(prefix);
              pw.print(mKey);
              pw.print("=");
              pw.print(mValue);
              pw.println();
          }
  
          /** Used for proto dumpsys */
          public void dumpProto(ProtoOutputStream proto, long tag) {
              proto.write(tag, mValue);
          }
      }
  
      /** Represents an float config value. */
      public static class FloatValue {
          private final String mKey;
          private final float mDefaultValue;
          private float mValue;
  
          /** Constructor, initialize with a config key and a default value. */
          public FloatValue(String key, float defaultValue) {
              mKey = key;
              mDefaultValue = defaultValue;
              mValue = mDefaultValue;
          }
  
          /** Read a value from {@link KeyValueListParser} */
          public void parse(KeyValueListParser parser) {
              mValue = parser.getFloat(mKey, mDefaultValue);
          }
  
          /** Return the config key. */
          public String getKey() {
              return mKey;
          }
  
          /** Return the default value. */
          public float getDefaultValue() {
              return mDefaultValue;
          }
  
          /** Return the actual config value. */
          public float getValue() {
              return mValue;
          }
  
          /** Overwrites with a value. */
          public void setValue(float value) {
              mValue = value;
          }
  
          /** Used for dumpsys */
          public void dump(PrintWriter pw, String prefix) {
              pw.print(prefix);
              pw.print(mKey);
              pw.print("=");
              pw.print(mValue);
              pw.println();
          }
  
          /** Used for proto dumpsys */
          public void dumpProto(ProtoOutputStream proto, long tag) {
              proto.write(tag, mValue);
          }
      }
  }
  