| /* |
| * 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.voicemailomtp.mail; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * A utility class for creating and modifying Strings that are tagged and packed together. |
| * |
| * 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. |
| * |
| * 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 HashMap<String, String> mExploded; |
| private static final HashMap<String, String> EMPTY_MAP = new HashMap<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 HashMap<String,String>(mExploded); |
| } |
| |
| /** |
| * Read out all values into a map. |
| */ |
| private static HashMap<String, String> explode(String packed) { |
| if (packed == null || packed.length() == 0) { |
| return EMPTY_MAP; |
| } |
| HashMap<String, String> map = new HashMap<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. |
| */ |
| static public class Builder { |
| HashMap<String, String> mMap; |
| |
| /** |
| * Create a builder that's empty (for filling) |
| */ |
| public Builder() { |
| mMap = new HashMap<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(); |
| } |
| } |
| } |