diff options
8 files changed, 177 insertions, 36 deletions
diff --git a/core/java/android/pim/vcard/VCardParser.java b/core/java/android/pim/vcard/VCardParser.java index 0ffdb6c..462e22c 100644 --- a/core/java/android/pim/vcard/VCardParser.java +++ b/core/java/android/pim/vcard/VCardParser.java @@ -21,9 +21,23 @@ import java.io.IOException; import java.io.InputStream; public abstract class VCardParser { + public static final int PARSER_MODE_DEFAULT = 0; + /** + * The parser should ignore "AGENT" properties and nested vCard structure. + */ + public static final int PARSER_MODE_SCAN = 1; + protected final int mParserMode; protected boolean mCanceled; - + + public VCardParser() { + mParserMode = PARSER_MODE_DEFAULT; + } + + public VCardParser(int parserMode) { + mParserMode = parserMode; + } + /** * Parses the given stream and send the VCard data into VCardBuilderBase object. * diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java index 748ea37..251db68 100644 --- a/core/java/android/pim/vcard/VCardParser_V21.java +++ b/core/java/android/pim/vcard/VCardParser_V21.java @@ -15,11 +15,11 @@ */ package android.pim.vcard; +import android.pim.vcard.exception.VCardAgentNotSupportedException; import android.pim.vcard.exception.VCardException; import android.pim.vcard.exception.VCardInvalidCommentLineException; import android.pim.vcard.exception.VCardInvalidLineException; import android.pim.vcard.exception.VCardNestedException; -import android.pim.vcard.exception.VCardNotSupportedException; import android.pim.vcard.exception.VCardVersionException; import android.util.Log; @@ -91,8 +91,15 @@ public class VCardParser_V21 extends VCardParser { // In order to reduce warning message as much as possible, we hold the value which made Logger // emit a warning message. - protected HashSet<String> mWarningValueMap = new HashSet<String>(); - + protected HashSet<String> mUnknownTypeMap = new HashSet<String>(); + protected HashSet<String> mUnknownValueMap = new HashSet<String>(); + + // It seems Windows Mobile 6.5 uses "AGENT" property with completely wrong usage. + // We should just ignore just one line. + // e.g. + // "AGENT;CHARSET=SHIFT_JIS:some text" + private boolean mIgnoreAgentLine = false; + // Just for debugging private long mTimeTotal; private long mTimeReadStartRecord; @@ -106,21 +113,41 @@ public class VCardParser_V21 extends VCardParser { private long mTimeHandleMiscPropertyValue; private long mTimeHandleQuotedPrintable; private long mTimeHandleBase64; - + /** * Create a new VCard parser. */ public VCardParser_V21() { - super(); + this(null, PARSER_MODE_DEFAULT); + } + + public VCardParser_V21(int parserMode) { + this(null, parserMode); } public VCardParser_V21(VCardSourceDetector detector) { - super(); - if (detector != null && detector.getType() == VCardSourceDetector.TYPE_FOMA) { - mNestCount = 1; + this(detector, PARSER_MODE_DEFAULT); + } + + /** + * TODO: Merge detector and parser mode. + */ + public VCardParser_V21(VCardSourceDetector detector, int parserMode) { + super(parserMode); + if (detector != null) { + final int type = detector.getType(); + if (type == VCardSourceDetector.TYPE_FOMA) { + mNestCount = 1; + } else if (type == VCardSourceDetector.TYPE_JAPANESE_MOBILE_PHONE) { + mIgnoreAgentLine = true; + } + } + + if (parserMode == PARSER_MODE_SCAN) { + mIgnoreAgentLine = true; } } - + /** * Parse the file at the given position * vcard_file = [wsls] vcard [wsls] @@ -160,8 +187,8 @@ public class VCardParser_V21 extends VCardParser { protected boolean isValidPropertyName(String propertyName) { if (!(sAvailablePropertyNameSetV21.contains(propertyName.toUpperCase()) || propertyName.startsWith("X-")) && - !mWarningValueMap.contains(propertyName)) { - mWarningValueMap.add(propertyName); + !mUnknownTypeMap.contains(propertyName)) { + mUnknownTypeMap.add(propertyName); Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName); } return true; @@ -554,9 +581,9 @@ public class VCardParser_V21 extends VCardParser { protected void handleType(final String ptypeval) { String upperTypeValue = ptypeval; if (!(sKnownTypeSet.contains(upperTypeValue) || upperTypeValue.startsWith("X-")) && - !mWarningValueMap.contains(ptypeval)) { - mWarningValueMap.add(ptypeval); - Log.w(LOG_TAG, "Type unsupported by vCard 2.1: " + ptypeval); + !mUnknownTypeMap.contains(ptypeval)) { + mUnknownTypeMap.add(ptypeval); + Log.w(LOG_TAG, "TYPE unsupported by vCard 2.1: " + ptypeval); } if (mBuilder != null) { mBuilder.propertyParamType("TYPE"); @@ -567,15 +594,16 @@ public class VCardParser_V21 extends VCardParser { /** * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word */ - protected void handleValue(final String pvalueval) throws VCardException { - if (sKnownValueSet.contains(pvalueval.toUpperCase()) || - pvalueval.startsWith("X-")) { - if (mBuilder != null) { - mBuilder.propertyParamType("VALUE"); - mBuilder.propertyParamValue(pvalueval); - } - } else { - throw new VCardException("Unknown value \"" + pvalueval + "\""); + protected void handleValue(final String pvalueval) { + if (!sKnownValueSet.contains(pvalueval.toUpperCase()) && + pvalueval.startsWith("X-") && + !mUnknownValueMap.contains(pvalueval)) { + mUnknownValueMap.add(pvalueval); + Log.w(LOG_TAG, "VALUE unsupported by vCard 2.1: " + pvalueval); + } + if (mBuilder != null) { + mBuilder.propertyParamType("VALUE"); + mBuilder.propertyParamValue(pvalueval); } } @@ -800,9 +828,14 @@ public class VCardParser_V21 extends VCardParser { * items *CRLF "END" [ws] ":" [ws] "VCARD" * */ - protected void handleAgent(String propertyValue) throws VCardException { - throw new VCardNotSupportedException("AGENT Property is not supported now."); - /* This is insufficient support. Also, AGENT Property is very rare. + protected void handleAgent(final String propertyValue) throws VCardException { + if (mIgnoreAgentLine) { + return; + } else { + throw new VCardAgentNotSupportedException("AGENT Property is not supported now."); + } + /* This is insufficient support. Also, AGENT Property is very rare and really hard to + understand the content. Ignore it for now. String[] strArray = propertyValue.split(":", 2); @@ -819,7 +852,7 @@ public class VCardParser_V21 extends VCardParser { /** * For vCard 3.0. */ - protected String maybeUnescapeText(String text) { + protected String maybeUnescapeText(final String text) { return text; } @@ -827,11 +860,11 @@ public class VCardParser_V21 extends VCardParser { * Returns unescaped String if the character should be unescaped. Return null otherwise. * e.g. In vCard 2.1, "\;" should be unescaped into ";" while "\x" should not be. */ - protected String maybeUnescapeCharacter(char ch) { + protected String maybeUnescapeCharacter(final char ch) { return unescapeCharacter(ch); } - public static String unescapeCharacter(char ch) { + public static String unescapeCharacter(final char ch) { // Original vCard 2.1 specification does not allow transformation // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous implementation of // this class allowed them, so keep it as is. @@ -843,7 +876,7 @@ public class VCardParser_V21 extends VCardParser { } @Override - public boolean parse(InputStream is, VCardBuilder builder) + public boolean parse(final InputStream is, final VCardBuilder builder) throws IOException, VCardException { return parse(is, VCardConfig.DEFAULT_CHARSET, builder); } diff --git a/core/java/android/pim/vcard/VCardParser_V30.java b/core/java/android/pim/vcard/VCardParser_V30.java index 86e7625..3dd467c 100644 --- a/core/java/android/pim/vcard/VCardParser_V30.java +++ b/core/java/android/pim/vcard/VCardParser_V30.java @@ -72,6 +72,11 @@ public class VCardParser_V30 extends VCardParser_V21 { mStrictParsing = strictParsing; } + public VCardParser_V30(int parseMode) { + super(parseMode); + mStrictParsing = false; + } + @Override protected int getVersion() { return VCardConfig.FLAG_V30; @@ -87,18 +92,18 @@ public class VCardParser_V30 extends VCardParser_V21 { if (!(sAcceptablePropsWithParam.contains(propertyName) || acceptablePropsWithoutParam.contains(propertyName) || propertyName.startsWith("X-")) && - !mWarningValueMap.contains(propertyName)) { - mWarningValueMap.add(propertyName); + !mUnknownTypeMap.contains(propertyName)) { + mUnknownTypeMap.add(propertyName); Log.w(LOG_TAG, "Property name unsupported by vCard 3.0: " + propertyName); } return true; } - + @Override protected boolean isValidEncoding(String encoding) { return sAcceptableEncodingV30.contains(encoding.toUpperCase()); } - + @Override protected String getLine() throws IOException { if (mPreviousLine != null) { diff --git a/core/java/android/pim/vcard/exception/VCardAgentNotSupportedException.java b/core/java/android/pim/vcard/exception/VCardAgentNotSupportedException.java new file mode 100644 index 0000000..e72c7df --- /dev/null +++ b/core/java/android/pim/vcard/exception/VCardAgentNotSupportedException.java @@ -0,0 +1,27 @@ +/* + * 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 android.pim.vcard.exception; + +public class VCardAgentNotSupportedException extends VCardNotSupportedException { + public VCardAgentNotSupportedException() { + super(); + } + + public VCardAgentNotSupportedException(String message) { + super(message); + } + +}
\ No newline at end of file diff --git a/tests/AndroidTests/res/raw/v21_winmo_65.vcf b/tests/AndroidTests/res/raw/v21_winmo_65.vcf new file mode 100644 index 0000000..f380d0d --- /dev/null +++ b/tests/AndroidTests/res/raw/v21_winmo_65.vcf @@ -0,0 +1,10 @@ +BEGIN:VCARD
+VERSION:2.1
+N:Example;;;;
+FN:Example
+ANNIVERSARY;VALUE=DATE:20091010
+AGENT:Invalid line which must be handled correctly.
+X-CLASS:PUBLIC
+X-REDUCTION:
+X-NO:
+END:VCARD
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java index 917b18e..a9775fa 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java @@ -23,7 +23,6 @@ import android.pim.vcard.VCardParser_V21; import android.pim.vcard.VCardParser_V30; import android.pim.vcard.exception.VCardException; import android.test.AndroidTestCase; -import android.util.Log; import junit.framework.TestCase; @@ -56,6 +55,12 @@ public class PropertyNodesVerifier extends VNodeBuilder { verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vCardType); } + public void verify(int resId, int vCardType, final VCardParser vCardParser) + throws IOException, VCardException { + verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), + vCardType, vCardParser); + } + public void verify(InputStream is, int vCardType) throws IOException, VCardException { final VCardParser vCardParser; if (VCardConfig.isV30(vCardType)) { @@ -63,6 +68,11 @@ public class PropertyNodesVerifier extends VNodeBuilder { } else { vCardParser = new VCardParser_V21(); } + verify(is, vCardType, vCardParser); + } + + public void verify(InputStream is, int vCardType, final VCardParser vCardParser) + throws IOException, VCardException { try { vCardParser.parse(is, this); } finally { diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java index 169ea71..b1fccaa 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java @@ -16,7 +16,10 @@ package com.android.unit_tests.vcard; +import android.content.ContentValues; import android.pim.vcard.VCardConfig; +import android.pim.vcard.VCardParser; +import android.pim.vcard.VCardParser_V21; import android.pim.vcard.exception.VCardException; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.CommonDataKinds.Email; @@ -999,6 +1002,35 @@ public class VCardImporterTests extends VCardTestsBase { verifier.verify(R.raw.v21_multiple_entry, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS); } + public void testIgnoreAgentV21_Parse() throws IOException, VCardException { + PropertyNodesVerifier verifier = new PropertyNodesVerifier(this); + ContentValues contentValuesForValue = new ContentValues(); + contentValuesForValue.put("VALUE", "DATE"); + verifier.addPropertyNodesVerifierElem() + .addNodeWithOrder("VERSION", "2.1") + .addNodeWithOrder("N", Arrays.asList("Example", "", "", "", "")) + .addNodeWithOrder("FN", "Example") + .addNodeWithOrder("ANNIVERSARY", "20091010", contentValuesForValue) + .addNodeWithOrder("AGENT", "") + .addNodeWithOrder("X-CLASS", "PUBLIC") + .addNodeWithOrder("X-REDUCTION", "") + .addNodeWithOrder("X-NO", ""); + + // Only scan mode lets vCard parser accepts invalid AGENT lines like above. + verifier.verify(R.raw.v21_winmo_65, V21, + new VCardParser_V21(VCardParser.PARSER_MODE_SCAN)); + } + + public void testIgnoreAgentV21() throws IOException, VCardException { + ImportVerifier verifier = new ImportVerifier(); + ImportVerifierElem elem = verifier.addImportVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "Example") + .put(StructuredName.DISPLAY_NAME, "Example"); + verifier.verify(R.raw.v21_winmo_65, V21, + new VCardParser_V21(VCardParser.PARSER_MODE_SCAN)); + } + public void testPagerV30_Parse() throws IOException, VCardException { PropertyNodesVerifier verifier = new PropertyNodesVerifier(this); verifier.addPropertyNodesVerifierElem() diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java index bd4d13a..6176f5c 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java +++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java @@ -455,6 +455,11 @@ class CustomMockContext extends MockContext { verify(getContext().getResources().openRawResource(resId), vCardType); } + public void verify(int resId, int vCardType, final VCardParser vCardParser) + throws IOException, VCardException { + verify(getContext().getResources().openRawResource(resId), vCardType, vCardParser); + } + public void verify(InputStream is, int vCardType) throws IOException, VCardException { final VCardParser vCardParser; if (VCardConfig.isV30(vCardType)) { @@ -462,6 +467,11 @@ class CustomMockContext extends MockContext { } else { vCardParser = new VCardParser_V21(); } + verify(is, vCardType, vCardParser); + } + + public void verify(InputStream is, int vCardType, final VCardParser vCardParser) + throws IOException, VCardException { VCardDataBuilder builder = new VCardDataBuilder(null, null, false, vCardType, null); builder.addEntryHandler(this); |