1345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein/* 2345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Copyright (C) 2010 The Android Open Source Project 3345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 4345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Licensed under the Apache License, Version 2.0 (the "License"); 5345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * you may not use this file except in compliance with the License. 6345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * You may obtain a copy of the License at 7345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 8345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * http://www.apache.org/licenses/LICENSE-2.0 9345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 10345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Unless required by applicable law or agreed to in writing, software 11345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * distributed under the License is distributed on an "AS IS" BASIS, 12345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * See the License for the specific language governing permissions and 14345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * limitations under the License. 15345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 16345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 17345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinpackage com.android.emailcommon.mail; 18345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 19345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.util.HashMap; 20345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.util.Map; 21345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 22345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein/** 23345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * A utility class for creating and modifying Strings that are tagged and packed together. 24345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 25345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Uses non-printable (control chars) for internal delimiters; Intended for regular displayable 26345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * strings only, so please use base64 or other encoding if you need to hide any binary data here. 27345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * 28345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Binary compatible with Address.pack() format, which should migrate to use this code. 29345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 30345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinpublic class PackedString { 31345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 32345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 33345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Packing format is: 34345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * element : [ value ] or [ value TAG-DELIMITER tag ] 35345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * packed-string : [ element ] [ ELEMENT-DELIMITER [ element ] ]* 36345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 37345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private static final char DELIMITER_ELEMENT = '\1'; 38345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private static final char DELIMITER_TAG = '\2'; 39345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 40345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private String mString; 41345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private HashMap<String, String> mExploded; 42345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private static final HashMap<String, String> EMPTY_MAP = new HashMap<String, String>(); 43345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 44345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 45345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Create a packed string using an already-packed string (e.g. from database) 46345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param string packed string 47345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 48345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public PackedString(String string) { 49345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein mString = string; 50345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein mExploded = null; 51345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 52345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 53345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 54345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Get the value referred to by a given tag. If the tag does not exist, return null. 55345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param tag identifier of string of interest 56345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return returns value, or null if no string is found 57345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 58345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public String get(String tag) { 59345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (mExploded == null) { 60345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein mExploded = explode(mString); 61345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 62345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return mExploded.get(tag); 63345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 64345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 65345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 66345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Return a map of all of the values referred to by a given tag. This is a shallow 67345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * copy, don't edit the values. 68345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return a map of the values in the packed string 69345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 70345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public Map<String, String> unpack() { 71345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (mExploded == null) { 72345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein mExploded = explode(mString); 73345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 74345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return new HashMap<String,String>(mExploded); 75345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 76345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 77345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 78345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Read out all values into a map. 79345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 80345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein private static HashMap<String, String> explode(String packed) { 81345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (packed == null || packed.length() == 0) { 82345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return EMPTY_MAP; 83345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 84345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein HashMap<String, String> map = new HashMap<String, String>(); 85345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 86345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein int length = packed.length(); 87345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein int elementStartIndex = 0; 88345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein int elementEndIndex = 0; 89345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein int tagEndIndex = packed.indexOf(DELIMITER_TAG); 90345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 91345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein while (elementStartIndex < length) { 92345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein elementEndIndex = packed.indexOf(DELIMITER_ELEMENT, elementStartIndex); 93345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (elementEndIndex == -1) { 94345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein elementEndIndex = length; 95345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 96345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein String tag; 97345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein String value; 98345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (tagEndIndex == -1 || elementEndIndex <= tagEndIndex) { 99345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // in this case the DELIMITER_PERSONAL is in a future pair (or not found) 100345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // so synthesize a positional tag for the value, and don't update tagEndIndex 101345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein value = packed.substring(elementStartIndex, elementEndIndex); 102345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein tag = Integer.toString(map.size()); 103345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } else { 104345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein value = packed.substring(elementStartIndex, tagEndIndex); 105345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein tag = packed.substring(tagEndIndex + 1, elementEndIndex); 106345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein // scan forward for next tag, if any 107345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein tagEndIndex = packed.indexOf(DELIMITER_TAG, elementEndIndex + 1); 108345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 109345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein map.put(tag, value); 110345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein elementStartIndex = elementEndIndex + 1; 111345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 112345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 113345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return map; 114345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 115345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 116345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 117345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Builder class for creating PackedString values. Can also be used for editing existing 118345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * PackedString representations. 119345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 120345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein static public class Builder { 121345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein HashMap<String, String> mMap; 122345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 123345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 124345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Create a builder that's empty (for filling) 125345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 126345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public Builder() { 127345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein mMap = new HashMap<String, String>(); 128345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 129345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 130345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 131345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Create a builder using the values of an existing PackedString (for editing). 132345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 133345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public Builder(String packed) { 134345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein mMap = explode(packed); 135345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 136345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 137345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 138345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Add a tagged value 139345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param tag identifier of string of interest 140345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param value the value to record in this position. null to delete entry. 141345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 142345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public void put(String tag, String value) { 143345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (value == null) { 144345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein mMap.remove(tag); 145345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } else { 146345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein mMap.put(tag, value); 147345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 148345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 149345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 150345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 151345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Get the value referred to by a given tag. If the tag does not exist, return null. 152345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @param tag identifier of string of interest 153345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * @return returns value, or null if no string is found 154345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 155345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public String get(String tag) { 156345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return mMap.get(tag); 157345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 158345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein 159345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein /** 160345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Pack the values and return a single, encoded string 161345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */ 162345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein @Override 163345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein public String toString() { 164345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein StringBuilder sb = new StringBuilder(); 165345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein for (Map.Entry<String,String> entry : mMap.entrySet()) { 166345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein if (sb.length() > 0) { 167345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein sb.append(DELIMITER_ELEMENT); 168345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 169345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein sb.append(entry.getValue()); 170345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein sb.append(DELIMITER_TAG); 171345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein sb.append(entry.getKey()); 172345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 173345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein return sb.toString(); 174345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 175345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein } 176345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein} 177