Create a PackedString utility class
* We can use this for meeting request information which will start out with one or two pieces of information, but might grow in the future. * Binary compatible with Address.pack() format, so we can eventually combine code.
This commit is contained in:
parent
4ce8150977
commit
5afa187791
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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.email.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
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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.email.mail;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests of PackedString
|
||||
*
|
||||
* You can run this entire test case with:
|
||||
* runtest -c com.android.email.mail.PackedStringTests email
|
||||
*/
|
||||
public class PackedStringTests extends TestCase {
|
||||
/** Note: copied from actual class */
|
||||
private static final char DELIMITER_ELEMENT = '\1';
|
||||
private static final char DELIMITER_TAG = '\2';
|
||||
|
||||
// A packed string with tags and values
|
||||
private static final String PACKED_STRING_TAGGED = "val1" + DELIMITER_TAG + "tag1" +
|
||||
DELIMITER_ELEMENT + "val2" + DELIMITER_TAG + "tag2" +
|
||||
DELIMITER_ELEMENT + "val3" + DELIMITER_TAG + "tag3" +
|
||||
DELIMITER_ELEMENT + "val4" + DELIMITER_TAG + "tag4";
|
||||
|
||||
public void testPackedString() {
|
||||
// Start with a packed string and make sure we can extract the correct Strings
|
||||
PackedString ps = new PackedString(PACKED_STRING_TAGGED);
|
||||
assertEquals("val1", ps.get("tag1"));
|
||||
assertEquals("val2", ps.get("tag2"));
|
||||
assertEquals("val3", ps.get("tag3"));
|
||||
assertEquals("val4", ps.get("tag4"));
|
||||
assertNull(ps.get("tag100"));
|
||||
}
|
||||
|
||||
// test the builder in "create mode"
|
||||
public void testPackedStringBuilderCreate() {
|
||||
PackedString.Builder b = new PackedString.Builder();
|
||||
b.put("tag1", "value1");
|
||||
b.put("tag2", "value2");
|
||||
b.put("tag3", "value3");
|
||||
b.put("tag4", "value4");
|
||||
// can't use simple string compare on output, because order not guaranteed
|
||||
// for now, we'll just pump into another one and test results
|
||||
String packedOut = b.toString();
|
||||
PackedString.Builder b2 = new PackedString.Builder(packedOut);
|
||||
assertEquals("value1", b2.get("tag1"));
|
||||
assertEquals("value2", b2.get("tag2"));
|
||||
assertEquals("value3", b2.get("tag3"));
|
||||
assertEquals("value4", b2.get("tag4"));
|
||||
assertNull(b2.get("tag100"));
|
||||
}
|
||||
|
||||
// test the builder in "edit mode"
|
||||
public void testPackedStringBuilderEdit() {
|
||||
// Start with a Builder based on a non-empty packed string
|
||||
PackedString.Builder b = new PackedString.Builder(PACKED_STRING_TAGGED);
|
||||
// Test readback in-place
|
||||
assertEquals("val1", b.get("tag1"));
|
||||
assertEquals("val2", b.get("tag2"));
|
||||
assertEquals("val3", b.get("tag3"));
|
||||
assertEquals("val4", b.get("tag4"));
|
||||
assertNull(b.get("tag100"));
|
||||
|
||||
// Test modifications in-place
|
||||
b.put("tag2", "TWO"); // edit
|
||||
b.put("tag3", null); // delete
|
||||
b.put("tag5", "value5"); // add
|
||||
// Read-back modifications in place
|
||||
assertEquals("val1", b.get("tag1"));
|
||||
assertEquals("TWO", b.get("tag2")); // edited
|
||||
assertEquals(null, b.get("tag3")); // deleted
|
||||
assertEquals("val4", b.get("tag4"));
|
||||
assertEquals("value5", b.get("tag5")); // added
|
||||
assertNull(b.get("tag100"));
|
||||
|
||||
// Confirm resulting packed string is as-expected
|
||||
String packedOut = b.toString();
|
||||
PackedString.Builder b2 = new PackedString.Builder(packedOut);
|
||||
assertEquals("val1", b2.get("tag1"));
|
||||
assertEquals("TWO", b2.get("tag2"));
|
||||
assertEquals(null, b2.get("tag3"));
|
||||
assertEquals("val4", b2.get("tag4"));
|
||||
assertEquals("value5", b2.get("tag5"));
|
||||
assertNull(b2.get("tag100"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue