diff options
Diffstat (limited to 'java/com/android/voicemail/impl/mail/PackedString.java')
| -rw-r--r-- | java/com/android/voicemail/impl/mail/PackedString.java | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/java/com/android/voicemail/impl/mail/PackedString.java b/java/com/android/voicemail/impl/mail/PackedString.java new file mode 100644 index 000000000..701dab62b --- /dev/null +++ b/java/com/android/voicemail/impl/mail/PackedString.java @@ -0,0 +1,172 @@ +/* + * 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 com.android.voicemail.impl.mail; + +import android.util.ArrayMap; +import java.util.Map; + +/** + * A utility class for creating and modifying Strings that are tagged and packed together. + * + * <p>Uses non-printable (control chars) for internal delimiters; Intended for regular displayable + * strings only, so please use base64 or other encoding if you need to hide any binary data here. + * + * <p>Binary compatible with Address.pack() format, which should migrate to use this code. + */ +public class PackedString { + + /** + * Packing format is: element : [ value ] or [ value TAG-DELIMITER tag ] packed-string : [ element + * ] [ ELEMENT-DELIMITER [ element ] ]* + */ + private static final char DELIMITER_ELEMENT = '\1'; + + private static final char DELIMITER_TAG = '\2'; + + private String mString; + private ArrayMap<String, String> mExploded; + private static final ArrayMap<String, String> EMPTY_MAP = new ArrayMap<String, String>(); + + /** + * Create a packed string using an already-packed string (e.g. from database) + * + * @param string packed string + */ + public PackedString(String string) { + mString = string; + mExploded = null; + } + + /** + * Get the value referred to by a given tag. If the tag does not exist, return null. + * + * @param tag identifier of string of interest + * @return returns value, or null if no string is found + */ + public String get(String tag) { + if (mExploded == null) { + mExploded = explode(mString); + } + return mExploded.get(tag); + } + + /** + * Return a map of all of the values referred to by a given tag. This is a shallow copy, don't + * edit the values. + * + * @return a map of the values in the packed string + */ + public Map<String, String> unpack() { + if (mExploded == null) { + mExploded = explode(mString); + } + return new ArrayMap<String, String>(mExploded); + } + + /** Read out all values into a map. */ + private static ArrayMap<String, String> explode(String packed) { + if (packed == null || packed.length() == 0) { + return EMPTY_MAP; + } + ArrayMap<String, String> map = new ArrayMap<String, String>(); + + int length = packed.length(); + int elementStartIndex = 0; + int elementEndIndex = 0; + int tagEndIndex = packed.indexOf(DELIMITER_TAG); + + while (elementStartIndex < length) { + elementEndIndex = packed.indexOf(DELIMITER_ELEMENT, elementStartIndex); + if (elementEndIndex == -1) { + elementEndIndex = length; + } + String tag; + String value; + if (tagEndIndex == -1 || elementEndIndex <= tagEndIndex) { + // in this case the DELIMITER_PERSONAL is in a future pair (or not found) + // so synthesize a positional tag for the value, and don't update tagEndIndex + value = packed.substring(elementStartIndex, elementEndIndex); + tag = Integer.toString(map.size()); + } else { + value = packed.substring(elementStartIndex, tagEndIndex); + tag = packed.substring(tagEndIndex + 1, elementEndIndex); + // scan forward for next tag, if any + tagEndIndex = packed.indexOf(DELIMITER_TAG, elementEndIndex + 1); + } + map.put(tag, value); + elementStartIndex = elementEndIndex + 1; + } + + return map; + } + + /** + * Builder class for creating PackedString values. Can also be used for editing existing + * PackedString representations. + */ + public static class Builder { + ArrayMap<String, String> mMap; + + /** Create a builder that's empty (for filling) */ + public Builder() { + mMap = new ArrayMap<String, String>(); + } + + /** Create a builder using the values of an existing PackedString (for editing). */ + public Builder(String packed) { + mMap = explode(packed); + } + + /** + * Add a tagged value + * + * @param tag identifier of string of interest + * @param value the value to record in this position. null to delete entry. + */ + public void put(String tag, String value) { + if (value == null) { + mMap.remove(tag); + } else { + mMap.put(tag, value); + } + } + + /** + * Get the value referred to by a given tag. If the tag does not exist, return null. + * + * @param tag identifier of string of interest + * @return returns value, or null if no string is found + */ + public String get(String tag) { + return mMap.get(tag); + } + + /** Pack the values and return a single, encoded string */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Map.Entry<String, String> entry : mMap.entrySet()) { + if (sb.length() > 0) { + sb.append(DELIMITER_ELEMENT); + } + sb.append(entry.getValue()); + sb.append(DELIMITER_TAG); + sb.append(entry.getKey()); + } + return sb.toString(); + } + } +} |
