diff options
author | Android (Google) Code Review <android-gerrit@google.com> | 2009-07-17 14:09:00 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2009-07-17 14:09:00 -0700 |
commit | 7ff6b74cf275fad0c0ee7929fb9cd1d6dc116299 (patch) | |
tree | 062691bdc73be6aeb13a7aa8b407e07ef5023c4f /tests | |
parent | 228b2f3a813e93413a0f9e2f29dfbfc54590a356 (diff) | |
parent | 7674b81a05a8aa0b7a1be1bb04e041f6d2106fe2 (diff) | |
download | frameworks_base-7ff6b74cf275fad0c0ee7929fb9cd1d6dc116299.zip frameworks_base-7ff6b74cf275fad0c0ee7929fb9cd1d6dc116299.tar.gz frameworks_base-7ff6b74cf275fad0c0ee7929fb9cd1d6dc116299.tar.bz2 |
Merge change 6736
* changes:
Refactor VCard-related code.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/AndroidTests/res/raw/v21_simple_1.vcf | 3 | ||||
-rw-r--r-- | tests/AndroidTests/res/raw/v21_simple_2.vcf | 3 | ||||
-rw-r--r-- | tests/AndroidTests/res/raw/v21_simple_3.vcf (renamed from tests/AndroidTests/res/raw/v21_simple.vcf) | 0 | ||||
-rw-r--r-- | tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java | 322 | ||||
-rw-r--r-- | tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java (renamed from tests/AndroidTests/src/com/android/unit_tests/VCardTests.java) | 207 | ||||
-rw-r--r-- | tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java | 30 | ||||
-rw-r--r-- | tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java | 313 |
7 files changed, 850 insertions, 28 deletions
diff --git a/tests/AndroidTests/res/raw/v21_simple_1.vcf b/tests/AndroidTests/res/raw/v21_simple_1.vcf new file mode 100644 index 0000000..6aabb4c --- /dev/null +++ b/tests/AndroidTests/res/raw/v21_simple_1.vcf @@ -0,0 +1,3 @@ +BEGIN:VCARD
+N:Ando;Roid;
+END:VCARD
diff --git a/tests/AndroidTests/res/raw/v21_simple_2.vcf b/tests/AndroidTests/res/raw/v21_simple_2.vcf new file mode 100644 index 0000000..f0d5ab5 --- /dev/null +++ b/tests/AndroidTests/res/raw/v21_simple_2.vcf @@ -0,0 +1,3 @@ +BEGIN:VCARD
+FN:Ando Roid
+END:VCARD
diff --git a/tests/AndroidTests/res/raw/v21_simple.vcf b/tests/AndroidTests/res/raw/v21_simple_3.vcf index beddabb..beddabb 100644 --- a/tests/AndroidTests/res/raw/v21_simple.vcf +++ b/tests/AndroidTests/res/raw/v21_simple_3.vcf diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java new file mode 100644 index 0000000..0ee74df --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2009 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.unit_tests.vcard; + +import android.content.ContentValues; + +import org.apache.commons.codec.binary.Base64; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +/** + * @hide old class just for test + */ +public class PropertyNode { + public String propName; + public String propValue; + public List<String> propValue_vector; + + /** Store value as byte[],after decode. + * Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc. + */ + public byte[] propValue_bytes; + + /** param store: key=paramType, value=paramValue + * Note that currently PropertyNode class does not support multiple param-values + * defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as + * one String value like "A,B", not ["A", "B"]... + * TODO: fix this. + */ + public ContentValues paramMap; + + /** Only for TYPE=??? param store. */ + public Set<String> paramMap_TYPE; + + /** Store group values. Used only in VCard. */ + public Set<String> propGroupSet; + + public PropertyNode() { + propName = ""; + propValue = ""; + propValue_vector = new ArrayList<String>(); + paramMap = new ContentValues(); + paramMap_TYPE = new HashSet<String>(); + propGroupSet = new HashSet<String>(); + } + + public PropertyNode( + String propName, String propValue, List<String> propValue_vector, + byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE, + Set<String> propGroupSet) { + if (propName != null) { + this.propName = propName; + } else { + this.propName = ""; + } + if (propValue != null) { + this.propValue = propValue; + } else { + this.propValue = ""; + } + if (propValue_vector != null) { + this.propValue_vector = propValue_vector; + } else { + this.propValue_vector = new ArrayList<String>(); + } + this.propValue_bytes = propValue_bytes; + if (paramMap != null) { + this.paramMap = paramMap; + } else { + this.paramMap = new ContentValues(); + } + if (paramMap_TYPE != null) { + this.paramMap_TYPE = paramMap_TYPE; + } else { + this.paramMap_TYPE = new HashSet<String>(); + } + if (propGroupSet != null) { + this.propGroupSet = propGroupSet; + } else { + this.propGroupSet = new HashSet<String>(); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PropertyNode)) { + return false; + } + + PropertyNode node = (PropertyNode)obj; + + if (propName == null || !propName.equals(node.propName)) { + return false; + } else if (!paramMap.equals(node.paramMap)) { + return false; + } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) { + return false; + } else if (!propGroupSet.equals(node.propGroupSet)) { + return false; + } + + if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) { + return true; + } else { + // Log.d("@@@", propValue + ", " + node.propValue); + if (!propValue.equals(node.propValue)) { + return false; + } + + // The value in propValue_vector is not decoded even if it should be + // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector + // is 1, the encoded value is stored in propValue, so we do not have to + // check it. + return (propValue_vector.equals(node.propValue_vector) || + propValue_vector.size() == 1 || + node.propValue_vector.size() == 1); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("propName: "); + builder.append(propName); + builder.append(", paramMap: "); + builder.append(paramMap.toString()); + builder.append(", propmMap_TYPE: "); + builder.append(paramMap_TYPE.toString()); + builder.append(", propGroupSet: "); + builder.append(propGroupSet.toString()); + if (propValue_vector != null && propValue_vector.size() > 1) { + builder.append(", propValue_vector size: "); + builder.append(propValue_vector.size()); + } + if (propValue_bytes != null) { + builder.append(", propValue_bytes size: "); + builder.append(propValue_bytes.length); + } + builder.append(", propValue: "); + builder.append(propValue); + return builder.toString(); + } + + /** + * Encode this object into a string which can be decoded. + */ + public String encode() { + // PropertyNode#toString() is for reading, not for parsing in the future. + // We construct appropriate String here. + StringBuilder builder = new StringBuilder(); + if (propName.length() > 0) { + builder.append("propName:["); + builder.append(propName); + builder.append("],"); + } + int size = propGroupSet.size(); + if (size > 0) { + Set<String> set = propGroupSet; + builder.append("propGroup:["); + int i = 0; + for (String group : set) { + // We do not need to double quote groups. + // group = 1*(ALPHA / DIGIT / "-") + builder.append(group); + if (i < size - 1) { + builder.append(","); + } + i++; + } + builder.append("],"); + } + + if (paramMap.size() > 0 || paramMap_TYPE.size() > 0) { + ContentValues values = paramMap; + builder.append("paramMap:["); + size = paramMap.size(); + int i = 0; + for (Entry<String, Object> entry : values.valueSet()) { + // Assuming param-key does not contain NON-ASCII nor symbols. + // + // According to vCard 3.0: + // param-name = iana-token / x-name + builder.append(entry.getKey()); + + // param-value may contain any value including NON-ASCIIs. + // We use the following replacing rule. + // \ -> \\ + // , -> \, + // In String#replaceAll(), "\\\\" means a single backslash. + builder.append("="); + builder.append(entry.getValue().toString() + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size -1) { + builder.append(","); + } + i++; + } + + Set<String> set = paramMap_TYPE; + size = paramMap_TYPE.size(); + if (i > 0 && size > 0) { + builder.append(","); + } + i = 0; + for (String type : set) { + builder.append("TYPE="); + builder.append(type + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size - 1) { + builder.append(","); + } + i++; + } + builder.append("],"); + } + + size = propValue_vector.size(); + if (size > 0) { + builder.append("propValue:["); + List<String> list = propValue_vector; + for (int i = 0; i < size; i++) { + builder.append(list.get(i) + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size -1) { + builder.append(","); + } + } + builder.append("],"); + } + + return builder.toString(); + } + + public static PropertyNode decode(String encodedString) { + PropertyNode propertyNode = new PropertyNode(); + String trimed = encodedString.trim(); + if (trimed.length() == 0) { + return propertyNode; + } + String[] elems = trimed.split("],"); + + for (String elem : elems) { + int index = elem.indexOf('['); + String name = elem.substring(0, index - 1); + Pattern pattern = Pattern.compile("(?<!\\\\),"); + String[] values = pattern.split(elem.substring(index + 1), -1); + if (name.equals("propName")) { + propertyNode.propName = values[0]; + } else if (name.equals("propGroupSet")) { + for (String value : values) { + propertyNode.propGroupSet.add(value); + } + } else if (name.equals("paramMap")) { + ContentValues paramMap = propertyNode.paramMap; + Set<String> paramMap_TYPE = propertyNode.paramMap_TYPE; + for (String value : values) { + String[] tmp = value.split("=", 2); + String mapKey = tmp[0]; + // \, -> , + // \\ -> \ + // In String#replaceAll(), "\\\\" means a single backslash. + String mapValue = + tmp[1].replaceAll("\\\\,", ",").replaceAll("\\\\\\\\", "\\\\"); + if (mapKey.equalsIgnoreCase("TYPE")) { + paramMap_TYPE.add(mapValue); + } else { + paramMap.put(mapKey, mapValue); + } + } + } else if (name.equals("propValue")) { + StringBuilder builder = new StringBuilder(); + List<String> list = propertyNode.propValue_vector; + int length = values.length; + for (int i = 0; i < length; i++) { + String normValue = values[i] + .replaceAll("\\\\,", ",") + .replaceAll("\\\\\\\\", "\\\\"); + list.add(normValue); + builder.append(normValue); + if (i < length - 1) { + builder.append(";"); + } + } + propertyNode.propValue = builder.toString(); + } + } + + // At this time, QUOTED-PRINTABLE is already decoded to Java String. + // We just need to decode BASE64 String to binary. + String encoding = propertyNode.paramMap.getAsString("ENCODING"); + if (encoding != null && + (encoding.equalsIgnoreCase("BASE64") || + encoding.equalsIgnoreCase("B"))) { + propertyNode.propValue_bytes = + Base64.decodeBase64(propertyNode.propValue_vector.get(0).getBytes()); + } + + return propertyNode; + } +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/VCardTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java index b7f562d..200d9c1 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/VCardTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java @@ -14,36 +14,130 @@ * limitations under the License. */ -package com.android.unit_tests; +package com.android.unit_tests.vcard; import android.content.ContentValues; -import android.syncml.pim.PropertyNode; -import android.syncml.pim.VDataBuilder; -import android.syncml.pim.VNode; -import android.syncml.pim.vcard.VCardException; -import android.syncml.pim.vcard.VCardParser_V21; -import android.syncml.pim.vcard.VCardParser_V30; +import android.pim.vcard.ContactStruct; +import android.pim.vcard.EntryHandler; +import android.pim.vcard.VCardBuilder; +import android.pim.vcard.VCardBuilderCollection; +import android.pim.vcard.VCardConfig; +import android.pim.vcard.VCardDataBuilder; +import android.pim.vcard.VCardParser; +import android.pim.vcard.VCardParser_V21; +import android.pim.vcard.VCardParser_V30; +import android.pim.vcard.exception.VCardException; import android.test.AndroidTestCase; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -import java.util.Vector; +import java.util.List; +import java.util.Map; public class VCardTests extends AndroidTestCase { + // TODO: Use EntityIterator, which is added in Eclair. + private static class EntryHolder implements EntryHandler { + public List<ContactStruct> contacts = new ArrayList<ContactStruct>(); + public void onEntryCreated(ContactStruct contactStruct) { + contacts.add(contactStruct); + } + public void onFinal() { + } + } + + static void verify(ContactStruct expected, ContactStruct actual) { + if (!equalsString(expected.getName(), actual.getName())) { + fail(String.format("Names do not equal: \"%s\" != \"%s\"", + expected.getName(), actual.getName())); + } + if (!equalsString( + expected.getPhoneticName(), actual.getPhoneticName())) { + fail(String.format("Phonetic names do not equal: \"%s\" != \"%s\"", + expected.getPhoneticName(), actual.getPhoneticName())); + } + { + final byte[] expectedPhotoBytes = expected.getPhotoBytes(); + final byte[] actualPhotoBytes = actual.getPhotoBytes(); + if (!((expectedPhotoBytes == null && actualPhotoBytes == null) || + Arrays.equals(expectedPhotoBytes, actualPhotoBytes))) { + fail("photoBytes is not equal."); + } + } + verifyInternal(expected.getNotes(), actual.getNotes(), "notes"); + verifyInternal(expected.getPhoneList(), actual.getPhoneList(), "phones"); + verifyInternal(expected.getContactMethodList(), actual.getContactMethodList(), + "contact lists"); + verifyInternal(expected.getOrganizationList(), actual.getOrganizationList(), + "organizations"); + { + final Map<String, List<String>> expectedMap = + expected.getExtensionMap(); + final Map<String, List<String>> actualMap = + actual.getExtensionMap(); + if (verifySize((expectedMap == null ? 0 : expectedMap.size()), + (actualMap == null ? 0 : actualMap.size()), "extensions") > 0) { + for (String key : expectedMap.keySet()) { + if (!actualMap.containsKey(key)) { + fail(String.format( + "Actual does not have %s extension while expected has", + key)); + } + final List<String> expectedList = expectedMap.get(key); + final List<String> actualList = actualMap.get(key); + verifyInternal(expectedList, actualList, + String.format("extension \"%s\"", key)); + } + } + } + } + + private static boolean equalsString(String a, String b) { + if (a == null || a.length() == 0) { + return b == null || b.length() == 0; + } else { + return a.equals(b); + } + } + + private static int verifySize(int expectedSize, int actualSize, String name) { + if (expectedSize != actualSize) { + fail(String.format("Size of %s is different: %d != %d", + name, expectedSize, actualSize)); + } + return expectedSize; + } + + private static <T> void verifyInternal(final List<T> expected, final List<T> actual, + String name) { + if(verifySize((expected == null ? 0 : expected.size()), + (actual == null ? 0 : actual.size()), name) > 0) { + int size = expected.size(); + for (int i = 0; i < size; i++) { + final T expectedObj = expected.get(i); + final T actualObj = actual.get(i); + if (!expected.equals(actual)) { + fail(String.format("The %i %s are different: %s != %s", + i, name, expectedObj, actualObj)); + } + } + } + } + private class PropertyNodesVerifier { - private HashMap<String, Vector<PropertyNode>> mPropertyNodeMap; + private HashMap<String, ArrayList<PropertyNode>> mPropertyNodeMap; public PropertyNodesVerifier(PropertyNode... nodes) { - mPropertyNodeMap = new HashMap<String, Vector<PropertyNode>>(); + mPropertyNodeMap = new HashMap<String, ArrayList<PropertyNode>>(); for (PropertyNode propertyNode : nodes) { String propName = propertyNode.propName; - Vector<PropertyNode> expectedNodes = + ArrayList<PropertyNode> expectedNodes = mPropertyNodeMap.get(propName); if (expectedNodes == null) { - expectedNodes = new Vector<PropertyNode>(); + expectedNodes = new ArrayList<PropertyNode>(); mPropertyNodeMap.put(propName, expectedNodes); } expectedNodes.add(propertyNode); @@ -53,7 +147,7 @@ public class VCardTests extends AndroidTestCase { public void verify(VNode vnode) { for (PropertyNode propertyNode : vnode.propList) { String propName = propertyNode.propName; - Vector<PropertyNode> nodes = mPropertyNodeMap.get(propName); + ArrayList<PropertyNode> nodes = mPropertyNodeMap.get(propName); if (nodes == null) { fail("Unexpected propName \"" + propName + "\" exists."); } @@ -81,8 +175,8 @@ public class VCardTests extends AndroidTestCase { } } if (mPropertyNodeMap.size() != 0) { - Vector<String> expectedProps = new Vector<String>(); - for (Vector<PropertyNode> nodes : mPropertyNodeMap.values()) { + ArrayList<String> expectedProps = new ArrayList<String>(); + for (ArrayList<PropertyNode> nodes : mPropertyNodeMap.values()) { for (PropertyNode node : nodes) { expectedProps.add(node.propName); } @@ -93,25 +187,82 @@ public class VCardTests extends AndroidTestCase { } } - public void testV21SimpleCase() throws IOException, VCardException { - VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); - InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple); + public void testV21SimpleCase1_1() throws IOException, VCardException { + VCardParser parser = new VCardParser_V21(); + VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH); + EntryHolder holder = new EntryHolder(); + builder.addEntryHandler(holder); + InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_1); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); - assertEquals(1, builder.vNodeList.size()); + assertEquals(1, holder.contacts.size()); + verify(new ContactStruct("Roid Ando", null, + null, null, null, null, null, null), + holder.contacts.get(0)); + } + + public void testV21SimpleCase1_2() throws IOException, VCardException { + VCardParser parser = new VCardParser_V21(); + VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_JAPANESE); + EntryHolder holder = new EntryHolder(); + builder.addEntryHandler(holder); + InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_1); + assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); + is.close(); + assertEquals(1, holder.contacts.size()); + verify(new ContactStruct("Ando Roid", null, + null, null, null, null, null, null), + holder.contacts.get(0)); + } + + public void testV21SimpleCase2() throws IOException, VCardException { + VCardParser parser = new VCardParser_V21(); + VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH); + EntryHolder holder = new EntryHolder(); + builder.addEntryHandler(holder); + InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_2); + assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); + is.close(); + assertEquals(1, holder.contacts.size()); + verify(new ContactStruct("Ando Roid", null, + null, null, null, null, null, null), + holder.contacts.get(0)); + } + + public void testV21SimpleCase3() throws IOException, VCardException { + VCardParser parser = new VCardParser_V21(); + VCardDataBuilder builder1 = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH); + EntryHolder holder = new EntryHolder(); + builder1.addEntryHandler(holder); + VNodeBuilder builder2 = new VNodeBuilder(); + VCardBuilderCollection collection = + new VCardBuilderCollection( + new ArrayList<VCardBuilder>(Arrays.asList(builder1, builder2))); + InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_3); + assertEquals(true, parser.parse(is,"ISO-8859-1", collection)); + is.close(); + + assertEquals(1, builder2.vNodeList.size()); + VNode vnode = builder2.vNodeList.get(0); PropertyNodesVerifier verifier = new PropertyNodesVerifier( new PropertyNode("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", ""), null, null, null, null), new PropertyNode("FN", "Ando Roid", null, null, null, null, null)); - verifier.verify(builder.vNodeList.get(0)); + verifier.verify(vnode); + + // FN is prefered. + assertEquals(1, holder.contacts.size()); + ContactStruct actual = holder.contacts.get(0); + verify(new ContactStruct("Ando Roid", null, + null, null, null, null, null, null), + actual); } - + public void testV21BackslashCase() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v21_backslash); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); @@ -129,7 +280,7 @@ public class VCardTests extends AndroidTestCase { public void testV21ComplicatedCase() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v21_complicated); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); @@ -585,7 +736,7 @@ public class VCardTests extends AndroidTestCase { public void testV21Japanese1() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v21_japanese_1); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); @@ -616,7 +767,7 @@ public class VCardTests extends AndroidTestCase { public void testV21Japanese2() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v21_japanese_2); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); @@ -660,7 +811,7 @@ public class VCardTests extends AndroidTestCase { public void testV21MultipleEntryCase() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V21(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v21_multiple_entry); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); @@ -741,7 +892,7 @@ public class VCardTests extends AndroidTestCase { public void testV30SimpleCase() throws IOException, VCardException { VCardParser_V21 parser = new VCardParser_V30(); - VDataBuilder builder = new VDataBuilder(); + VNodeBuilder builder = new VNodeBuilder(); InputStream is = getContext().getResources().openRawResource(R.raw.v30_simple); assertEquals(true, parser.parse(is,"ISO-8859-1", builder)); is.close(); diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java new file mode 100644 index 0000000..3eb827b --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2009 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.unit_tests.vcard; + +import java.util.ArrayList; + +/** + * @hide old class. Just for testing + */ +public class VNode { + public String VName; + + public ArrayList<PropertyNode> propList = new ArrayList<PropertyNode>(); + + /** 0:parse over. 1:parsing. */ + public int parseStatus = 1; +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java new file mode 100644 index 0000000..6d69223 --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2009 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.unit_tests.vcard; + +import android.content.ContentValues; +import android.pim.vcard.VCardBuilder; +import android.pim.vcard.VCardConfig; +import android.util.CharsetUtils; +import android.util.Log; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.net.QuotedPrintableCodec; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * Store the parse result to custom datastruct: VNode, PropertyNode + * Maybe several vcard instance, so use vNodeList to store. + * VNode: standy by a vcard instance. + * PropertyNode: standy by a property line of a card. + * @hide old class, just for testing use + */ +public class VNodeBuilder implements VCardBuilder { + static private String LOG_TAG = "VDATABuilder"; + + /** + * If there's no other information available, this class uses this charset for encoding + * byte arrays. + */ + static public String TARGET_CHARSET = "UTF-8"; + + /** type=VNode */ + public List<VNode> vNodeList = new ArrayList<VNode>(); + private int mNodeListPos = 0; + private VNode mCurrentVNode; + private PropertyNode mCurrentPropNode; + private String mCurrentParamType; + + /** + * The charset using which VParser parses the text. + */ + private String mSourceCharset; + + /** + * The charset with which byte array is encoded to String. + */ + private String mTargetCharset; + + private boolean mStrictLineBreakParsing; + + public VNodeBuilder() { + this(VCardConfig.DEFAULT_CHARSET, TARGET_CHARSET, false); + } + + public VNodeBuilder(String charset, boolean strictLineBreakParsing) { + this(null, charset, strictLineBreakParsing); + } + + /** + * @hide sourceCharset is temporal. + */ + public VNodeBuilder(String sourceCharset, String targetCharset, + boolean strictLineBreakParsing) { + if (sourceCharset != null) { + mSourceCharset = sourceCharset; + } else { + mSourceCharset = VCardConfig.DEFAULT_CHARSET; + } + if (targetCharset != null) { + mTargetCharset = targetCharset; + } else { + mTargetCharset = TARGET_CHARSET; + } + mStrictLineBreakParsing = strictLineBreakParsing; + } + + public void start() { + } + + public void end() { + } + + // Note: I guess that this code assumes the Record may nest like this: + // START:VPOS + // ... + // START:VPOS2 + // ... + // END:VPOS2 + // ... + // END:VPOS + // + // However the following code has a bug. + // When error occurs after calling startRecord(), the entry which is probably + // the cause of the error remains to be in vNodeList, while endRecord() is not called. + // + // I leave this code as is since I'm not familiar with vcalendar specification. + // But I believe we should refactor this code in the future. + // Until this, the last entry has to be removed when some error occurs. + public void startRecord(String type) { + + VNode vnode = new VNode(); + vnode.parseStatus = 1; + vnode.VName = type; + // I feel this should be done in endRecord(), but it cannot be done because of + // the reason above. + vNodeList.add(vnode); + mNodeListPos = vNodeList.size() - 1; + mCurrentVNode = vNodeList.get(mNodeListPos); + } + + public void endRecord() { + VNode endNode = vNodeList.get(mNodeListPos); + endNode.parseStatus = 0; + while(mNodeListPos > 0){ + mNodeListPos--; + if((vNodeList.get(mNodeListPos)).parseStatus == 1) + break; + } + mCurrentVNode = vNodeList.get(mNodeListPos); + } + + public void startProperty() { + mCurrentPropNode = new PropertyNode(); + } + + public void endProperty() { + mCurrentVNode.propList.add(mCurrentPropNode); + } + + public void propertyName(String name) { + mCurrentPropNode.propName = name; + } + + // Used only in VCard. + public void propertyGroup(String group) { + mCurrentPropNode.propGroupSet.add(group); + } + + public void propertyParamType(String type) { + mCurrentParamType = type; + } + + public void propertyParamValue(String value) { + if (mCurrentParamType == null || + mCurrentParamType.equalsIgnoreCase("TYPE")) { + mCurrentPropNode.paramMap_TYPE.add(value); + } else { + mCurrentPropNode.paramMap.put(mCurrentParamType, value); + } + + mCurrentParamType = null; + } + + private String encodeString(String originalString, String targetCharset) { + if (mSourceCharset.equalsIgnoreCase(targetCharset)) { + return originalString; + } + Charset charset = Charset.forName(mSourceCharset); + ByteBuffer byteBuffer = charset.encode(originalString); + // byteBuffer.array() "may" return byte array which is larger than + // byteBuffer.remaining(). Here, we keep on the safe side. + byte[] bytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(bytes); + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return null; + } + } + + private String handleOneValue(String value, String targetCharset, String encoding) { + if (encoding != null) { + if (encoding.equals("BASE64") || encoding.equals("B")) { + // Assume BASE64 is used only when the number of values is 1. + mCurrentPropNode.propValue_bytes = + Base64.decodeBase64(value.getBytes()); + return value; + } else if (encoding.equals("QUOTED-PRINTABLE")) { + String quotedPrintable = value + .replaceAll("= ", " ").replaceAll("=\t", "\t"); + String[] lines; + if (mStrictLineBreakParsing) { + lines = quotedPrintable.split("\r\n"); + } else { + StringBuilder builder = new StringBuilder(); + int length = quotedPrintable.length(); + ArrayList<String> list = new ArrayList<String>(); + for (int i = 0; i < length; i++) { + char ch = quotedPrintable.charAt(i); + if (ch == '\n') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else if (ch == '\r') { + list.add(builder.toString()); + builder = new StringBuilder(); + if (i < length - 1) { + char nextCh = quotedPrintable.charAt(i + 1); + if (nextCh == '\n') { + i++; + } + } + } else { + builder.append(ch); + } + } + String finalLine = builder.toString(); + if (finalLine.length() > 0) { + list.add(finalLine); + } + lines = list.toArray(new String[0]); + } + StringBuilder builder = new StringBuilder(); + for (String line : lines) { + if (line.endsWith("=")) { + line = line.substring(0, line.length() - 1); + } + builder.append(line); + } + byte[] bytes; + try { + bytes = builder.toString().getBytes(mSourceCharset); + } catch (UnsupportedEncodingException e1) { + Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset); + bytes = builder.toString().getBytes(); + } + + try { + bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes); + } catch (DecoderException e) { + Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); + return ""; + } + + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); + } + } + // Unknown encoding. Fall back to default. + } + return encodeString(value, targetCharset); + } + + public void propertyValues(List<String> values) { + if (values == null || values.size() == 0) { + mCurrentPropNode.propValue_bytes = null; + mCurrentPropNode.propValue_vector.clear(); + mCurrentPropNode.propValue_vector.add(""); + mCurrentPropNode.propValue = ""; + return; + } + + ContentValues paramMap = mCurrentPropNode.paramMap; + + String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET")); + String encoding = paramMap.getAsString("ENCODING"); + + if (targetCharset == null || targetCharset.length() == 0) { + targetCharset = mTargetCharset; + } + + for (String value : values) { + mCurrentPropNode.propValue_vector.add( + handleOneValue(value, targetCharset, encoding)); + } + + mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector); + } + + private String listToString(List<String> list){ + int size = list.size(); + if (size > 1) { + StringBuilder typeListB = new StringBuilder(); + for (String type : list) { + typeListB.append(type).append(";"); + } + int len = typeListB.length(); + if (len > 0 && typeListB.charAt(len - 1) == ';') { + return typeListB.substring(0, len - 1); + } + return typeListB.toString(); + } else if (size == 1) { + return list.get(0); + } else { + return ""; + } + } + + public String getResult(){ + return null; + } +} |