summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaisuke Miyakawa <dmiyakawa@google.com>2011-03-21 16:40:57 -0700
committerDaisuke Miyakawa <dmiyakawa@google.com>2011-03-22 16:55:07 -0700
commit48dd8e86a81d2ab40eb762975c8211c225002bf0 (patch)
tree58a95496803f05d1a019a2057d292b586c73da85
parent403c83bde6d05e6325e99eb89924f0a09e5df3bb (diff)
downloadvcard-48dd8e86a81d2ab40eb762975c8211c225002bf0.tar.gz
Tolerate nested vCard.
vCard 2.1 requires to handle nest (or grouping) while our parser has not supported it well. One support we have had is ignoring top element when FLAG_TORELATE_NEST is specified, which isn't appropriate. e.g. BEGIN:VCARD X-VENDER-SPECIFIC-META-INFO:XXXX ... BEGIN:VCARD (Actual content) END:VCARD END:VCARD This change lets the vCard lib import nested vCard. One problem around handling the case above is that, after having this change, we'll get two vCard elements instead of one, as the top element isn't ignored on parser level any more, while we don't want the top-level data. To solve the problem, this change also makes vCard importer ignore such empty data. This changes just flattens nested vCard. Caller needs to take care of grouping/nest case using VCardEntry#getChildren() if it wants to support grouping feature. - introduce "children" into VCardEntry, which enables users to handle nested vCard on their side. - make vCard parsers accept nest cases. - make vCard interpreters handle nest cases. - make VCardEntry ignore empty data during constructing insert opertions. - make tests accept nest cases. - add additional test cases for verifying more details. - add debug string capability. - remove codes for performance measurement. Bug: 4066223 Change-Id: Id8af659c2cc0bb0db59c8de239d9d95e9d440089
-rw-r--r--java/com/android/vcard/VCardConfig.java24
-rw-r--r--java/com/android/vcard/VCardConstants.java1
-rw-r--r--java/com/android/vcard/VCardEntry.java106
-rw-r--r--java/com/android/vcard/VCardEntryConstructor.java45
-rw-r--r--java/com/android/vcard/VCardEntryHandler.java14
-rw-r--r--java/com/android/vcard/VCardInterpreter.java17
-rw-r--r--java/com/android/vcard/VCardParserImpl_V21.java450
-rw-r--r--java/com/android/vcard/VCardParserImpl_V30.java40
-rw-r--r--java/com/android/vcard/VCardParser_V21.java4
-rw-r--r--java/com/android/vcard/VCardSourceDetector.java14
-rw-r--r--tests/res/raw/v21_nest.vcf13
-rw-r--r--tests/src/com/android/vcard/tests/VCardEntryTests.java161
-rw-r--r--tests/src/com/android/vcard/tests/VCardImporterNestTests.java35
-rw-r--r--tests/src/com/android/vcard/tests/VCardImporterTests.java2
-rw-r--r--tests/src/com/android/vcard/tests/VCardInterpreterTests.java192
-rw-r--r--tests/src/com/android/vcard/tests/VCardTestRunner.java3
-rw-r--r--tests/src/com/android/vcard/tests/testutils/ContentValuesVerifierElem.java31
-rw-r--r--tests/src/com/android/vcard/tests/testutils/ImportTestProvider.java3
-rw-r--r--tests/src/com/android/vcard/tests/testutils/PropertyNodesVerifier.java10
-rw-r--r--tests/src/com/android/vcard/tests/testutils/VCardVerifier.java1
-rw-r--r--tests/src/com/android/vcard/tests/testutils/VNode.java5
-rw-r--r--tests/src/com/android/vcard/tests/testutils/VNodeBuilder.java89
22 files changed, 836 insertions, 424 deletions
diff --git a/java/com/android/vcard/VCardConfig.java b/java/com/android/vcard/VCardConfig.java
index 5cc796b..fa1da7b 100644
--- a/java/com/android/vcard/VCardConfig.java
+++ b/java/com/android/vcard/VCardConfig.java
@@ -235,30 +235,6 @@ public class VCardConfig {
public static final int FLAG_REFRAIN_PHONE_NUMBER_FORMATTING = 0x02000000;
/**
- * <p>
- * For importer only. Ignored in exporter.
- * </p>
- * <p>
- * The flag indicating the parser should handle a nested vCard, in which vCard clause starts
- * in another vCard clause. Here's a typical example.
- * </p>
- * <pre class="prettyprint">BEGIN:VCARD
- * BEGIN:VCARD
- * VERSION:2.1
- * ...
- * END:VCARD
- * END:VCARD</pre>
- * <p>
- * The vCard 2.1 specification allows the nest, but also let parsers ignore nested entries,
- * while some mobile devices emit nested ones as primary data to be imported.
- * </p>
- * <p>
- * This flag forces a vCard parser to torelate such a nest and understand its content.
- * </p>
- */
- public static final int FLAG_TORELATE_NEST = 0x01000000;
-
- /**
* <P>
* The flag asking exporter to refrain image export.
* </P>
diff --git a/java/com/android/vcard/VCardConstants.java b/java/com/android/vcard/VCardConstants.java
index 20fabed..67c07b7 100644
--- a/java/com/android/vcard/VCardConstants.java
+++ b/java/com/android/vcard/VCardConstants.java
@@ -107,6 +107,7 @@ public class VCardConstants {
public static final String PARAM_TYPE_VOICE = "VOICE";
public static final String PARAM_TYPE_INTERNET = "INTERNET";
+ public static final String PARAM_VALUE = "VALUE";
public static final String PARAM_CHARSET = "CHARSET";
public static final String PARAM_ENCODING = "ENCODING";
diff --git a/java/com/android/vcard/VCardEntry.java b/java/com/android/vcard/VCardEntry.java
index afe1bfa..5c30e44 100644
--- a/java/com/android/vcard/VCardEntry.java
+++ b/java/com/android/vcard/VCardEntry.java
@@ -57,7 +57,7 @@ import java.util.Set;
public class VCardEntry {
private static final String LOG_TAG = VCardConstants.LOG_TAG;
- private final static int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK;
+ private static final int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK;
private static final Map<String, Integer> sImMap = new HashMap<String, Integer>();
@@ -381,7 +381,12 @@ public class VCardEntry {
}
}
- /* package */ static class Property {
+ /**
+ * TODO: implement better structure for Property. We also have PropertyData in
+ * VCardParserImpl_V21 for storing intermidate Property data.
+ * @hide public just for testing.
+ */
+ public static class Property {
private String mPropertyName;
private Map<String, Collection<String>> mParameterMap =
new HashMap<String, Collection<String>>();
@@ -407,10 +412,14 @@ public class VCardEntry {
values.add(paramValue);
}
- public void addToPropertyValueList(final String propertyValue) {
+ public void addPropertyValue(final String propertyValue) {
mPropertyValueList.add(propertyValue);
}
+ public void addPropertyValue(final String... propertyValueList) {
+ mPropertyValueList.addAll(Arrays.asList(propertyValueList));
+ }
+
public void setPropertyBytes(final byte[] propertyBytes) {
mPropertyBytes = propertyBytes;
}
@@ -429,6 +438,12 @@ public class VCardEntry {
mPropertyValueList.clear();
mPropertyBytes = null;
}
+
+ @Override
+ public String toString() {
+ return String.format("Name: %s, value: %s",
+ mPropertyName, Arrays.toString(mPropertyValueList.toArray(new String[0])));
+ }
}
// TODO(dmiyakawa): vCard 4.0 logically has multiple formatted names and we need to
@@ -475,6 +490,56 @@ public class VCardEntry {
private final int mVCardType;
private final Account mAccount;
+ private List<VCardEntry> mChildren;
+
+ public String getNameFieldDebugString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(String.format(
+ "Family: %s, Given: %s, Middle: %s, Prefix: %s, Suffix: %s\n",
+ mFamilyName, mGivenName, mMiddleName, mPrefix, mSuffix));
+ builder.append(String.format(
+ "Phonetic Family: %s, Phonetyc Given: %s, Phonetic Middle: %s\n",
+ mPhoneticFamilyName, mPhoneticGivenName, mPhoneticMiddleName));
+ builder.append(String.format("Phonetic Full: %s\n", mPhoneticFullName));
+ builder.append(String.format("Formatted: %s, Display Name: %s\n",
+ mFormattedName, mDisplayName));
+ if (mNickNameList != null) {
+ builder.append(String.format(
+ "Nick names: %s\n", Arrays.toString(mNickNameList.toArray(new String[0]))));
+ }
+ return builder.toString();
+ }
+
+ private String getStringFromCollection(String name, Collection<?> collection) {
+ return collection != null ?
+ String.format("%s: %s\n", name, Arrays.toString(collection.toArray())) : "";
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("hash: " + hashCode() + "\n");
+ builder.append(getNameFieldDebugString());
+ builder.append(getStringFromCollection("phone", mPhoneList));
+ builder.append(getStringFromCollection("email", mEmailList));
+ builder.append(getStringFromCollection("postal", mPostalList));
+ builder.append(getStringFromCollection("note", mNoteList));
+ builder.append(getStringFromCollection("organization", mOrganizationList));
+ builder.append(getStringFromCollection("im", mImList));
+ builder.append(getStringFromCollection("photo", mPhotoList));
+ builder.append(getStringFromCollection("website", mWebsiteList));
+ builder.append(getStringFromCollection("sip", mSipSet));
+ if (mChildren != null) {
+ int size = mChildren.size();
+ int[] hashArray = new int[size];
+ for (int i = 0; i < size; i++) {
+ hashArray[i] = mChildren.get(i).hashCode();
+ }
+ builder.append("children: " + Arrays.toString(hashArray) + "\n");
+ }
+ return "[[" + builder.toString().trim() + "]]";
+ }
+
public VCardEntry() {
this(VCardConfig.VCARD_TYPE_V21_GENERIC);
}
@@ -1106,6 +1171,13 @@ public class VCardEntry {
// Be careful when adding some logic here, as some blocks above may use "return".
}
+ public void addChild(VCardEntry child) {
+ if (mChildren == null) {
+ mChildren = new ArrayList<VCardEntry>();
+ }
+ mChildren.add(child);
+ }
+
private void handleAndroidCustomProperty(final List<String> customPropertyList) {
if (mAndroidCustomPropertyList == null) {
mAndroidCustomPropertyList = new ArrayList<List<String>>();
@@ -1153,6 +1225,22 @@ public class VCardEntry {
}
}
+ private boolean isEmpty(Collection<?> collection) {
+ return (collection == null || collection.size() == 0);
+ }
+
+ public boolean isEmpty() {
+ // TODO: should implement iterator for all collections so that we can share iteration
+ // logic for isEmpty(), constructInsertOperations(), and toString()
+ // TODO: handle mAndroidCustomPropertyList appropriately. This logic is fragile.
+ return (nameFieldsAreEmpty() && isEmpty(mNickNameList) && isEmpty(mNoteList)
+ && isEmpty(mPhoneList) && isEmpty(mEmailList) && isEmpty(mPostalList)
+ && isEmpty(mOrganizationList) && isEmpty(mImList) && isEmpty(mPhotoList)
+ && isEmpty(mWebsiteList) && isEmpty(mSipSet)
+ && isEmpty(mAndroidCustomPropertyList)
+ && TextUtils.isEmpty(mBirthday) && TextUtils.isEmpty(mAnniversary));
+ }
+
/**
* Constructs the list of insert operation for this object.
*
@@ -1173,6 +1261,10 @@ public class VCardEntry {
operationList = new ArrayList<ContentProviderOperation>();
}
+ if (isEmpty()) {
+ return operationList;
+ }
+
final int backReferenceIndex = operationList.size();
// After applying the batch the first result's Uri is returned so it is important that
@@ -1416,7 +1508,6 @@ public class VCardEntry {
}
public static VCardEntry buildFromResolver(ContentResolver resolver, Uri uri) {
-
return null;
}
@@ -1544,6 +1635,13 @@ public class VCardEntry {
return mWebsiteList;
}
+ /**
+ * @hide this interface may be changed for better support of vCard 4.0 (UID)
+ */
+ public final List<VCardEntry> getChildlen() {
+ return mChildren;
+ }
+
public String getDisplayName() {
if (mDisplayName == null) {
constructDisplayName();
diff --git a/java/com/android/vcard/VCardEntryConstructor.java b/java/com/android/vcard/VCardEntryConstructor.java
index 8d3610b..a6a2fe9 100644
--- a/java/com/android/vcard/VCardEntryConstructor.java
+++ b/java/com/android/vcard/VCardEntryConstructor.java
@@ -44,8 +44,12 @@ import java.util.List;
public class VCardEntryConstructor implements VCardInterpreter {
private static String LOG_TAG = VCardConstants.LOG_TAG;
+ /**
+ * Represents current stack of VCardEntry. Used to support nested vCard (vCard 2.1).
+ */
+ private final List<VCardEntry> mEntryStack = new ArrayList<VCardEntry>();
+ private VCardEntry mCurrentEntry;
private VCardEntry.Property mCurrentProperty = new VCardEntry.Property();
- private VCardEntry mCurrentVCardEntry;
private String mParamType;
// The charset using which {@link VCardInterpreter} parses the text.
@@ -58,9 +62,6 @@ public class VCardEntryConstructor implements VCardInterpreter {
private final int mVCardType;
private final Account mAccount;
- // For measuring performance.
- private long mTimePushIntoContentResolver;
-
private final List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();
public VCardEntryConstructor() {
@@ -114,25 +115,33 @@ public class VCardEntryConstructor implements VCardInterpreter {
}
public void clear() {
- mCurrentVCardEntry = null;
+ mCurrentEntry = null;
+ mEntryStack.clear();
mCurrentProperty = new VCardEntry.Property();
}
@Override
public void startEntry() {
- if (mCurrentVCardEntry != null) {
- Log.e(LOG_TAG, "Nested VCard code is not supported now.");
- }
- mCurrentVCardEntry = new VCardEntry(mVCardType, mAccount);
+ mCurrentEntry = new VCardEntry(mVCardType, mAccount);
+ mEntryStack.add(mCurrentEntry);
}
@Override
public void endEntry() {
- mCurrentVCardEntry.consolidateFields();
+ mCurrentEntry.consolidateFields();
for (VCardEntryHandler entryHandler : mEntryHandlers) {
- entryHandler.onEntryCreated(mCurrentVCardEntry);
+ entryHandler.onEntryCreated(mCurrentEntry);
}
- mCurrentVCardEntry = null;
+
+ final int size = mEntryStack.size();
+ if (size > 1) {
+ VCardEntry parent = mEntryStack.get(size - 2);
+ parent.addChild(mCurrentEntry);
+ mCurrentEntry = parent;
+ } else {
+ mCurrentEntry = null;
+ }
+ mEntryStack.remove(size - 1);
}
@Override
@@ -142,7 +151,7 @@ public class VCardEntryConstructor implements VCardInterpreter {
@Override
public void endProperty() {
- mCurrentVCardEntry.addProperty(mCurrentProperty);
+ mCurrentEntry.addProperty(mCurrentProperty);
}
@Override
@@ -222,16 +231,8 @@ public class VCardEntryConstructor implements VCardInterpreter {
}
for (final String value : values) {
- mCurrentProperty.addToPropertyValueList(
+ mCurrentProperty.addPropertyValue(
handleOneValue(value, mSourceCharset, targetCharset, encoding));
}
}
-
- /**
- * @hide
- */
- public void showPerformanceInfo() {
- Log.d(LOG_TAG, "time for insert ContactStruct to database: " +
- mTimePushIntoContentResolver + " ms");
- }
}
diff --git a/java/com/android/vcard/VCardEntryHandler.java b/java/com/android/vcard/VCardEntryHandler.java
index ef35a20..1c26dd1 100644
--- a/java/com/android/vcard/VCardEntryHandler.java
+++ b/java/com/android/vcard/VCardEntryHandler.java
@@ -31,7 +31,19 @@ public interface VCardEntryHandler {
public void onStart();
/**
- * The method called when one VCard entry is successfully created
+ * The method called when one vCard entry is created. Children come before their parent in
+ * nested vCard files.
+ *
+ * e.g.
+ * In the following vCard, the entry for "entry2" comes before one for "entry1".
+ * <code>
+ * BEGIN:VCARD
+ * N:entry1
+ * BEGIN:VCARD
+ * N:entry2
+ * END:VCARD
+ * END:VCARD
+ * </code>
*/
public void onEntryCreated(final VCardEntry entry);
diff --git a/java/com/android/vcard/VCardInterpreter.java b/java/com/android/vcard/VCardInterpreter.java
index 2d98764..01e5433 100644
--- a/java/com/android/vcard/VCardInterpreter.java
+++ b/java/com/android/vcard/VCardInterpreter.java
@@ -37,6 +37,8 @@ import java.util.List;
* e.g. group1.propName;paramName1=paramValue1;paramName2=paramValue2;propertyValue1;propertyValue2...
* </P>
*/
+// TODO: "on" should be appended for consistency..
+// e.g. onStart(), onEnd()
public interface VCardInterpreter {
/**
* Called when vCard interpretation started.
@@ -48,17 +50,26 @@ public interface VCardInterpreter {
*/
void end();
- /**
+ /**
* Called when parsing one vCard entry started.
* More specifically, this method is called when "BEGIN:VCARD" is read.
+ *
+ * This may be called before {@link #endEntry()} is called, as vCard 2.1 accepts nested vCard.
+ *
+ * <code>
+ * BEGIN:VCARD
+ * BEGIN:VCARD
+ * VERSION:2.1
+ * N:test;;;;
+ * END:VCARD
+ * END:VCARD
+ * </code>
*/
void startEntry();
/**
* Called when parsing one vCard entry ended.
* More specifically, this method is called when "END:VCARD" is read.
- * Note that {@link #startEntry()} may be called since
- * vCard (especially 2.1) allows nested vCard.
*/
void endEntry();
diff --git a/java/com/android/vcard/VCardParserImpl_V21.java b/java/com/android/vcard/VCardParserImpl_V21.java
index 9e08a19..c13e3a8 100644
--- a/java/com/android/vcard/VCardParserImpl_V21.java
+++ b/java/com/android/vcard/VCardParserImpl_V21.java
@@ -15,16 +15,15 @@
*/
package com.android.vcard;
-import android.text.TextUtils;
-import android.util.Log;
-
import com.android.vcard.exception.VCardAgentNotSupportedException;
import com.android.vcard.exception.VCardException;
import com.android.vcard.exception.VCardInvalidCommentLineException;
import com.android.vcard.exception.VCardInvalidLineException;
-import com.android.vcard.exception.VCardNestedException;
import com.android.vcard.exception.VCardVersionException;
+import android.text.TextUtils;
+import android.util.Log;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -131,13 +130,93 @@ import java.util.Set;
}
}
+ /**
+ * Stores param information for a property. Used with {@link PropertyData}.
+ *
+ * e.g. "LANGUAGE=jp" -> paramName = "LANGUAGE", paramvalue = "jp"
+ */
+ private class ParamPair {
+ public String paramName;
+ public String paramValue;
+ public ParamPair(String paramName, String paramValue) {
+ this.paramName = paramName;
+ this.paramValue = paramValue;
+ }
+ @Override
+ public String toString() {
+ return paramName + ", " + paramValue;
+ }
+ }
+
+ /**
+ * Intermediate data for storing property.
+ *
+ * Name, group and param are property encoded when this object is prepared.
+ * Value isn't encoded yet at this point.
+ */
+ protected class PropertyData {
+ private String mName;
+ private List<String> mGroupList;
+ private List<ParamPair> mParamList;
+ private String mRawValue;
+
+ public void setName(String name) throws VCardException {
+ if (mName != null) {
+ throw new VCardException(
+ String.format("Property name is re-defined " +
+ "(existing: %s, requested: %s", mName, name));
+ }
+ mName = name;
+ }
+
+ public void addGroup(String group) {
+ if (mGroupList == null) {
+ mGroupList = new ArrayList<String>();
+ }
+ mGroupList.add(group);
+ }
+
+ public void addParam(String paramName, String paramValue) {
+ if (mParamList == null) {
+ mParamList = new ArrayList<ParamPair>();
+ }
+ mParamList.add(new ParamPair(paramName, paramValue));
+ }
+
+ public void setRawValue(String rawValue) throws VCardException {
+ if (mRawValue != null) {
+ throw new VCardException(
+ String.format("Property value is re-defined " +
+ "(existing: %s, requested: %s", mRawValue, rawValue));
+ }
+ mRawValue = rawValue;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public List<String> getGroupList() {
+ return mGroupList;
+ }
+
+ public List<ParamPair> getParamList() {
+ return mParamList;
+ }
+
+ public String getRawValue() {
+ return mRawValue;
+ }
+ }
+
private static final String DEFAULT_ENCODING = "8BIT";
+ // TODO: remove this.
protected boolean mCanceled;
- protected VCardInterpreter mInterpreter;
-
protected final String mIntermediateCharset;
+ private VCardInterpreter mInterpreter;
+
/**
* <p>
* The encoding type for deconding byte streams. This member variable is
@@ -185,38 +264,11 @@ import java.util.Set;
protected final Set<String> mUnknownValueSet = new HashSet<String>();
- // In some cases, vCard is nested. Currently, we only consider the most
- // interior vCard data.
- // See v21_foma_1.vcf in test directory for more information.
- // TODO: Don't ignore by using count, but read all of information outside vCard.
- private int mNestCount;
-
- // Used only for parsing END:VCARD.
- private String mPreviousLine;
-
- // For measuring performance.
- private long mTimeTotal;
- private long mTimeReadStartRecord;
- private long mTimeReadEndRecord;
- private long mTimeStartProperty;
- private long mTimeEndProperty;
- private long mTimeParseItems;
- private long mTimeParseLineAndHandleGroup;
- private long mTimeParsePropertyValues;
- private long mTimeParseAdrOrgN;
- private long mTimeHandleMiscPropertyValue;
- private long mTimeHandleQuotedPrintable;
- private long mTimeHandleBase64;
-
public VCardParserImpl_V21() {
this(VCardConfig.VCARD_TYPE_DEFAULT);
}
public VCardParserImpl_V21(int vcardType) {
- if ((vcardType & VCardConfig.FLAG_TORELATE_NEST) != 0) {
- mNestCount = 1;
- }
-
mIntermediateCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
}
@@ -227,24 +279,14 @@ import java.util.Set;
*/
// <pre class="prettyprint">vcard_file = [wsls] vcard [wsls]</pre>
protected void parseVCardFile() throws IOException, VCardException {
- boolean readingFirstFile = true;
while (true) {
if (mCanceled) {
Log.i(LOG_TAG, "Cancel request has come. exitting parse operation.");
break;
}
- if (!parseOneVCard(readingFirstFile)) {
+ if (!parseOneVCard()) {
break;
}
- readingFirstFile = false;
- }
-
- if (mNestCount > 0) {
- boolean useCache = true;
- for (int i = 0; i < mNestCount; i++) {
- readEndVCard(useCache, true);
- useCache = false;
- }
}
}
@@ -290,40 +332,24 @@ import java.util.Set;
}
}
- /*
+ /**
+ * <code>
* vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
* items *CRLF
* "END" [ws] ":" [ws] "VCARD"
+ * </code>
*/
- private boolean parseOneVCard(boolean firstRead) throws IOException, VCardException {
- boolean allowGarbage = false;
- if (firstRead) {
- if (mNestCount > 0) {
- for (int i = 0; i < mNestCount; i++) {
- if (!readBeginVCard(allowGarbage)) {
- return false;
- }
- allowGarbage = true;
- }
- }
- }
+ private boolean parseOneVCard() throws IOException, VCardException {
+ // reset for this entire vCard.
+ mCurrentEncoding = DEFAULT_ENCODING;
+ boolean allowGarbage = false;
if (!readBeginVCard(allowGarbage)) {
return false;
}
- final long beforeStartEntry = System.currentTimeMillis();
mInterpreter.startEntry();
- mTimeReadStartRecord += System.currentTimeMillis() - beforeStartEntry;
-
- final long beforeParseItems = System.currentTimeMillis();
parseItems();
- mTimeParseItems += System.currentTimeMillis() - beforeParseItems;
-
- readEndVCard(true, false);
-
- final long beforeEndEntry = System.currentTimeMillis();
mInterpreter.endEntry();
- mTimeReadEndRecord += System.currentTimeMillis() - beforeEndEntry;
return true;
}
@@ -333,6 +359,7 @@ import java.util.Set;
* @throws VCardException
*/
protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
+ // TODO: use consructPropertyLine().
String line;
do {
while (true) {
@@ -349,20 +376,13 @@ import java.util.Set;
// Although vCard 2.1/3.0 specification does not allow lower cases,
// we found vCard file emitted by some external vCard expoter have such
// invalid Strings.
- // So we allow it.
- // e.g.
- // BEGIN:vCard
+ // e.g. BEGIN:vCard
if (length == 2 && strArray[0].trim().equalsIgnoreCase("BEGIN")
&& strArray[1].trim().equalsIgnoreCase("VCARD")) {
return true;
} else if (!allowGarbage) {
- if (mNestCount > 0) {
- mPreviousLine = line;
- return false;
- } else {
- throw new VCardException("Expected String \"BEGIN:VCARD\" did not come "
- + "(Instead, \"" + line + "\" came)");
- }
+ throw new VCardException("Expected String \"BEGIN:VCARD\" did not come "
+ + "(Instead, \"" + line + "\" came)");
}
} while (allowGarbage);
@@ -370,78 +390,28 @@ import java.util.Set;
}
/**
- * <p>
- * The arguments useCache and allowGarbase are usually true and false
- * accordingly when this function is called outside this function itself.
- * </p>
- *
- * @param useCache When true, line is obtained from mPreviousline.
- * Otherwise, getLine() is used.
- * @param allowGarbage When true, ignore non "END:VCARD" line.
- * @throws IOException
- * @throws VCardException
+ * Parses lines other than the first "BEGIN:VCARD". Takes care of "END:VCARD"n and
+ * "BEGIN:VCARD" in nested vCard.
*/
- protected void readEndVCard(boolean useCache, boolean allowGarbage) throws IOException,
- VCardException {
- String line;
- do {
- if (useCache) {
- // Though vCard specification does not allow lower cases,
- // some data may have them, so we allow it.
- line = mPreviousLine;
- } else {
- while (true) {
- line = getLine();
- if (line == null) {
- throw new VCardException("Expected END:VCARD was not found.");
- } else if (line.trim().length() > 0) {
- break;
- }
- }
- }
-
- String[] strArray = line.split(":", 2);
- if (strArray.length == 2 && strArray[0].trim().equalsIgnoreCase("END")
- && strArray[1].trim().equalsIgnoreCase("VCARD")) {
- return;
- } else if (!allowGarbage) {
- throw new VCardException("END:VCARD != \"" + mPreviousLine + "\"");
- }
- useCache = false;
- } while (allowGarbage);
- }
-
/*
* items = *CRLF item / item
+ *
+ * Note: BEGIN/END aren't include in the original spec while this method handles them.
*/
protected void parseItems() throws IOException, VCardException {
boolean ended = false;
- final long beforeBeginProperty = System.currentTimeMillis();
- mInterpreter.startProperty();
- mTimeStartProperty += System.currentTimeMillis() - beforeBeginProperty;
- ended = parseItem();
- if (!ended) {
- final long beforeEndProperty = System.currentTimeMillis();
- mInterpreter.endProperty();
- mTimeEndProperty += System.currentTimeMillis() - beforeEndProperty;
+ try {
+ ended = parseItem();
+ } catch (VCardInvalidCommentLineException e) {
+ Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
}
while (!ended) {
- final long beforeStartProperty = System.currentTimeMillis();
- mInterpreter.startProperty();
- mTimeStartProperty += System.currentTimeMillis() - beforeStartProperty;
try {
ended = parseItem();
} catch (VCardInvalidCommentLineException e) {
Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
- ended = false;
- }
-
- if (!ended) {
- final long beforeEndProperty = System.currentTimeMillis();
- mInterpreter.endProperty();
- mTimeEndProperty += System.currentTimeMillis() - beforeEndProperty;
}
}
}
@@ -453,50 +423,78 @@ import java.util.Set;
* "AGENT" [params] ":" vcard CRLF
*/
protected boolean parseItem() throws IOException, VCardException {
+ // Reset for an item.
mCurrentEncoding = DEFAULT_ENCODING;
final String line = getNonEmptyLine();
- long start = System.currentTimeMillis();
+ final PropertyData propertyData = constructPropertyData(line);
- String[] propertyNameAndValue = separateLineAndHandleGroup(line);
- if (propertyNameAndValue == null) {
- return true;
+ final String propertyNameUpper = propertyData.getName().toUpperCase();
+ final String propertyValue = propertyData.getRawValue();
+
+ if (propertyNameUpper.equals(VCardConstants.PROPERTY_BEGIN)) {
+ if (propertyValue.equalsIgnoreCase("VCARD")) {
+ handleNest();
+ } else {
+ throw new VCardException("Unknown BEGIN type: " + propertyValue);
+ }
+ } else if (propertyNameUpper.equals(VCardConstants.PROPERTY_END)) {
+ if (propertyValue.equalsIgnoreCase("VCARD")) {
+ return true; // Ended.
+ } else {
+ throw new VCardException("Unknown END type: " + propertyValue);
+ }
+ } else {
+ mInterpreter.startProperty();
+ sendPropertyLineMetaInfo(propertyData);
+ parseItemInter(propertyNameUpper, propertyValue);
+ mInterpreter.endProperty();
}
- if (propertyNameAndValue.length != 2) {
- throw new VCardInvalidLineException("Invalid line \"" + line + "\"");
+ return false;
+ }
+
+ private void sendPropertyLineMetaInfo(PropertyData propertyData) {
+ final List<String> groupList = propertyData.getGroupList();
+ if (groupList != null) {
+ for (String group : groupList) {
+ mInterpreter.propertyGroup(group);
+ }
}
- String propertyName = propertyNameAndValue[0].toUpperCase();
- String propertyValue = propertyNameAndValue[1];
+ mInterpreter.propertyName(propertyData.getName());
- mTimeParseLineAndHandleGroup += System.currentTimeMillis() - start;
+ final List<ParamPair> paramList = propertyData.getParamList();
+ if (paramList != null) {
+ for (ParamPair pair : paramList) {
+ mInterpreter.propertyParamType(pair.paramName);
+ mInterpreter.propertyParamValue(pair.paramValue);
+ }
+ }
+ }
- if (propertyName.equals("ADR") || propertyName.equals("ORG") || propertyName.equals("N")) {
- start = System.currentTimeMillis();
- handleMultiplePropertyValue(propertyName, propertyValue);
- mTimeParseAdrOrgN += System.currentTimeMillis() - start;
- return false;
- } else if (propertyName.equals("AGENT")) {
+ private void parseItemInter(String propertyNameUpper, String propertyValue)
+ throws IOException, VCardException {
+ if (propertyNameUpper.equals(VCardConstants.PROPERTY_ADR)
+ || propertyNameUpper.equals(VCardConstants.PROPERTY_ORG)
+ || propertyNameUpper.equals(VCardConstants.PROPERTY_N)) {
+ handleMultiplePropertyValue(propertyNameUpper, propertyValue);
+ } else if (propertyNameUpper.equals(VCardConstants.PROPERTY_AGENT)) {
handleAgent(propertyValue);
- return false;
- } else if (isValidPropertyName(propertyName)) {
- if (propertyName.equals("BEGIN")) {
- if (propertyValue.equals("VCARD")) {
- throw new VCardNestedException("This vCard has nested vCard data in it.");
- } else {
- throw new VCardException("Unknown BEGIN type: " + propertyValue);
- }
- } else if (propertyName.equals("VERSION") &&
+ } else if (isValidPropertyName(propertyNameUpper)) {
+ if (propertyNameUpper.equals(VCardConstants.PROPERTY_VERSION) &&
!propertyValue.equals(getVersionString())) {
throw new VCardVersionException("Incompatible version: " + propertyValue + " != "
+ getVersionString());
}
- start = System.currentTimeMillis();
- handlePropertyValue(propertyName, propertyValue);
- mTimeParsePropertyValues += System.currentTimeMillis() - start;
- return false;
+ handlePropertyValue(propertyNameUpper, propertyValue);
+ } else {
+ throw new VCardException("Unknown property name: \"" + propertyNameUpper + "\"");
}
+ }
- throw new VCardException("Unknown property name: \"" + propertyName + "\"");
+ private void handleNest() throws IOException, VCardException {
+ mInterpreter.startEntry();
+ parseItems();
+ mInterpreter.endEntry();
}
// For performance reason, the states for group and property name are merged into one.
@@ -505,8 +503,9 @@ import java.util.Set;
// vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not.
static private final int STATE_PARAMS_IN_DQUOTE = 2;
- protected String[] separateLineAndHandleGroup(String line) throws VCardException {
- final String[] propertyNameAndValue = new String[2];
+ protected PropertyData constructPropertyData(String line) throws VCardException {
+ final PropertyData propertyData = new PropertyData();
+
final int length = line.length();
if (length > 0 && line.charAt(0) == '#') {
throw new VCardInvalidCommentLineException();
@@ -523,34 +522,20 @@ import java.util.Set;
case STATE_GROUP_OR_PROPERTY_NAME: {
if (ch == ':') { // End of a property name.
final String propertyName = line.substring(nameIndex, i);
- if (propertyName.equalsIgnoreCase("END")) {
- mPreviousLine = line;
- return null;
- }
- mInterpreter.propertyName(propertyName);
- propertyNameAndValue[0] = propertyName;
- if (i < length - 1) {
- propertyNameAndValue[1] = line.substring(i + 1);
- } else {
- propertyNameAndValue[1] = "";
- }
- return propertyNameAndValue;
+ propertyData.setName(propertyName);
+ propertyData.setRawValue( i < length - 1 ? line.substring(i + 1) : "");
+ return propertyData;
} else if (ch == '.') { // Each group is followed by the dot.
final String groupName = line.substring(nameIndex, i);
if (groupName.length() == 0) {
Log.w(LOG_TAG, "Empty group found. Ignoring.");
} else {
- mInterpreter.propertyGroup(groupName);
+ propertyData.addGroup(groupName);
}
nameIndex = i + 1; // Next should be another group or a property name.
} else if (ch == ';') { // End of property name and beginneng of parameters.
final String propertyName = line.substring(nameIndex, i);
- if (propertyName.equalsIgnoreCase("END")) {
- mPreviousLine = line;
- return null;
- }
- mInterpreter.propertyName(propertyName);
- propertyNameAndValue[0] = propertyName;
+ propertyData.setName(propertyName);
nameIndex = i + 1;
state = STATE_PARAMS; // Start parameter parsing.
}
@@ -565,16 +550,12 @@ import java.util.Set;
}
state = STATE_PARAMS_IN_DQUOTE;
} else if (ch == ';') { // Starts another param.
- handleParams(line.substring(nameIndex, i));
+ handleParams(propertyData, line.substring(nameIndex, i));
nameIndex = i + 1;
} else if (ch == ':') { // End of param and beginenning of values.
- handleParams(line.substring(nameIndex, i));
- if (i < length - 1) {
- propertyNameAndValue[1] = line.substring(i + 1);
- } else {
- propertyNameAndValue[1] = "";
- }
- return propertyNameAndValue;
+ handleParams(propertyData, line.substring(nameIndex, i));
+ propertyData.setRawValue(i < length - 1 ? line.substring(i + 1) : "");
+ return propertyData;
}
break;
}
@@ -601,57 +582,56 @@ import java.util.Set;
* [ws] charsetval / "LANGUAGE" [ws] "=" [ws] langval / "X-" word [ws] "="
* [ws] word / knowntype
*/
- protected void handleParams(String params) throws VCardException {
+ protected void handleParams(PropertyData propertyData, String params)
+ throws VCardException {
final String[] strArray = params.split("=", 2);
if (strArray.length == 2) {
final String paramName = strArray[0].trim().toUpperCase();
String paramValue = strArray[1].trim();
if (paramName.equals("TYPE")) {
- handleType(paramValue);
+ handleType(propertyData, paramValue);
} else if (paramName.equals("VALUE")) {
- handleValue(paramValue);
+ handleValue(propertyData, paramValue);
} else if (paramName.equals("ENCODING")) {
- handleEncoding(paramValue);
+ handleEncoding(propertyData, paramValue);
} else if (paramName.equals("CHARSET")) {
- handleCharset(paramValue);
+ handleCharset(propertyData, paramValue);
} else if (paramName.equals("LANGUAGE")) {
- handleLanguage(paramValue);
+ handleLanguage(propertyData, paramValue);
} else if (paramName.startsWith("X-")) {
- handleAnyParam(paramName, paramValue);
+ handleAnyParam(propertyData, paramName, paramValue);
} else {
throw new VCardException("Unknown type \"" + paramName + "\"");
}
} else {
- handleParamWithoutName(strArray[0]);
+ handleParamWithoutName(propertyData, strArray[0]);
}
}
/**
* vCard 3.0 parser implementation may throw VCardException.
*/
- @SuppressWarnings("unused")
- protected void handleParamWithoutName(final String paramValue) throws VCardException {
- handleType(paramValue);
+ protected void handleParamWithoutName(PropertyData propertyData, final String paramValue) {
+ handleType(propertyData, paramValue);
}
/*
* ptypeval = knowntype / "X-" word
*/
- protected void handleType(final String ptypeval) {
+ protected void handleType(PropertyData propertyData, final String ptypeval) {
if (!(getKnownTypeSet().contains(ptypeval.toUpperCase())
|| ptypeval.startsWith("X-"))
&& !mUnknownTypeSet.contains(ptypeval)) {
mUnknownTypeSet.add(ptypeval);
Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval));
}
- mInterpreter.propertyParamType("TYPE");
- mInterpreter.propertyParamValue(ptypeval);
+ propertyData.addParam(VCardConstants.PARAM_TYPE, ptypeval);
}
/*
* pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
*/
- protected void handleValue(final String pvalueval) {
+ protected void handleValue(PropertyData propertyData, final String pvalueval) {
if (!(getKnownValueSet().contains(pvalueval.toUpperCase())
|| pvalueval.startsWith("X-")
|| mUnknownValueSet.contains(pvalueval))) {
@@ -659,18 +639,18 @@ import java.util.Set;
Log.w(LOG_TAG, String.format(
"The value unsupported by TYPE of %s: ", getVersion(), pvalueval));
}
- mInterpreter.propertyParamType("VALUE");
- mInterpreter.propertyParamValue(pvalueval);
+ propertyData.addParam(VCardConstants.PARAM_VALUE, pvalueval);
}
/*
* pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
*/
- protected void handleEncoding(String pencodingval) throws VCardException {
+ protected void handleEncoding(PropertyData propertyData, String pencodingval)
+ throws VCardException {
if (getAvailableEncodingSet().contains(pencodingval) ||
pencodingval.startsWith("X-")) {
- mInterpreter.propertyParamType("ENCODING");
- mInterpreter.propertyParamValue(pencodingval);
+ propertyData.addParam(VCardConstants.PARAM_ENCODING, pencodingval);
+ // Update encoding right away, as this is needed to understanding other params.
mCurrentEncoding = pencodingval;
} else {
throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
@@ -684,15 +664,15 @@ import java.util.Set;
* We allow any charset.
* </p>
*/
- protected void handleCharset(String charsetval) {
- mInterpreter.propertyParamType("CHARSET");
- mInterpreter.propertyParamValue(charsetval);
+ protected void handleCharset(PropertyData propertyData, String charsetval) {
+ propertyData.addParam(VCardConstants.PARAM_CHARSET, charsetval);
}
/**
* See also Section 7.1 of RFC 1521
*/
- protected void handleLanguage(String langval) throws VCardException {
+ protected void handleLanguage(PropertyData propertyData, String langval)
+ throws VCardException {
String[] strArray = langval.split("-");
if (strArray.length != 2) {
throw new VCardException("Invalid Language: \"" + langval + "\"");
@@ -711,8 +691,7 @@ import java.util.Set;
throw new VCardException("Invalid Language: \"" + langval + "\"");
}
}
- mInterpreter.propertyParamType(VCardConstants.PARAM_LANGUAGE);
- mInterpreter.propertyParamValue(langval);
+ propertyData.addParam(VCardConstants.PARAM_LANGUAGE, langval);
}
private boolean isAsciiLetter(char ch) {
@@ -725,24 +704,21 @@ import java.util.Set;
/**
* Mainly for "X-" type. This accepts any kind of type without check.
*/
- protected void handleAnyParam(String paramName, String paramValue) {
- mInterpreter.propertyParamType(paramName);
- mInterpreter.propertyParamValue(paramValue);
+ protected void handleAnyParam(
+ PropertyData propertyData, String paramName, String paramValue) {
+ propertyData.addParam(paramName, paramValue);
}
protected void handlePropertyValue(String propertyName, String propertyValue)
throws IOException, VCardException {
final String upperEncoding = mCurrentEncoding.toUpperCase();
if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) {
- final long start = System.currentTimeMillis();
final String result = getQuotedPrintable(propertyValue);
final ArrayList<String> v = new ArrayList<String>();
v.add(result);
mInterpreter.propertyValues(v);
- mTimeHandleQuotedPrintable += System.currentTimeMillis() - start;
} else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64)
|| upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) {
- final long start = System.currentTimeMillis();
// It is very rare, but some BASE64 data may be so big that
// OutOfMemoryError occurs. To ignore such cases, use try-catch.
try {
@@ -753,7 +729,6 @@ import java.util.Set;
Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
mInterpreter.propertyValues(null);
}
- mTimeHandleBase64 += System.currentTimeMillis() - start;
} else {
if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") ||
upperEncoding.startsWith("X-"))) {
@@ -813,11 +788,9 @@ import java.util.Set;
}
}
- final long start = System.currentTimeMillis();
ArrayList<String> v = new ArrayList<String>();
v.add(maybeUnescapeText(propertyValue));
mInterpreter.propertyValues(v);
- mTimeHandleMiscPropertyValue += System.currentTimeMillis() - start;
}
}
@@ -990,22 +963,6 @@ import java.util.Set;
}
}
- private void showPerformanceInfo() {
- Log.d(LOG_TAG, "Total parsing time: " + mTimeTotal + " ms");
- Log.d(LOG_TAG, "Total readLine time: " + mReader.getTotalmillisecond() + " ms");
- Log.d(LOG_TAG, "Time for handling the beggining of the record: " + mTimeReadStartRecord
- + " ms");
- Log.d(LOG_TAG, "Time for handling the end of the record: " + mTimeReadEndRecord + " ms");
- Log.d(LOG_TAG, "Time for parsing line, and handling group: " + mTimeParseLineAndHandleGroup
- + " ms");
- Log.d(LOG_TAG, "Time for parsing ADR, ORG, and N fields:" + mTimeParseAdrOrgN + " ms");
- Log.d(LOG_TAG, "Time for parsing property values: " + mTimeParsePropertyValues + " ms");
- Log.d(LOG_TAG, "Time for handling normal property values: " + mTimeHandleMiscPropertyValue
- + " ms");
- Log.d(LOG_TAG, "Time for handling Quoted-Printable: " + mTimeHandleQuotedPrintable + " ms");
- Log.d(LOG_TAG, "Time for handling Base64: " + mTimeHandleBase64 + " ms");
- }
-
/**
* @return {@link VCardConfig#VERSION_21}
*/
@@ -1060,11 +1017,6 @@ import java.util.Set;
if (mInterpreter != null) {
mInterpreter.end();
}
- mTimeTotal += System.currentTimeMillis() - start;
-
- if (VCardConfig.showPerformanceLog()) {
- showPerformanceInfo();
- }
}
public final void cancel() {
diff --git a/java/com/android/vcard/VCardParserImpl_V30.java b/java/com/android/vcard/VCardParserImpl_V30.java
index 8936fc4..d4055cf 100644
--- a/java/com/android/vcard/VCardParserImpl_V30.java
+++ b/java/com/android/vcard/VCardParserImpl_V30.java
@@ -150,25 +150,19 @@ import java.util.Set;
return super.readBeginVCard(allowGarbage);
}
- @Override
- protected void readEndVCard(boolean useCache, boolean allowGarbage)
- throws IOException, VCardException {
- // TODO: vCard 3.0 supports group.
- super.readEndVCard(useCache, allowGarbage);
- }
-
/**
* vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
*/
@Override
- protected void handleParams(final String params) throws VCardException {
+ protected void handleParams(PropertyData propertyData, final String params)
+ throws VCardException {
try {
- super.handleParams(params);
+ super.handleParams(propertyData, params);
} catch (VCardException e) {
// maybe IANA type
String[] strArray = params.split("=", 2);
if (strArray.length == 2) {
- handleAnyParam(strArray[0], strArray[1]);
+ handleAnyParam(propertyData, strArray[0], strArray[1]);
} else {
// Must not come here in the current implementation.
throw new VCardException(
@@ -178,14 +172,14 @@ import java.util.Set;
}
@Override
- protected void handleAnyParam(final String paramName, final String paramValue) {
- mInterpreter.propertyParamType(paramName);
- splitAndPutParamValue(paramValue);
+ protected void handleAnyParam(
+ PropertyData propertyData, final String paramName, final String paramValue) {
+ splitAndPutParam(propertyData, paramName, paramValue);
}
@Override
- protected void handleParamWithoutName(final String paramValue) {
- handleType(paramValue);
+ protected void handleParamWithoutName(PropertyData propertyData, final String paramValue) {
+ handleType(propertyData, paramValue);
}
/*
@@ -201,9 +195,8 @@ import java.util.Set;
* QSAFE-CHAR must not contain DQUOTE, including escaped one (\").
*/
@Override
- protected void handleType(final String paramValue) {
- mInterpreter.propertyParamType("TYPE");
- splitAndPutParamValue(paramValue);
+ protected void handleType(PropertyData propertyData, final String paramValue) {
+ splitAndPutParam(propertyData, VCardConstants.PARAM_TYPE, paramValue);
}
/**
@@ -218,7 +211,8 @@ import java.util.Set;
*
* QSAFE-CHAR must not contain DQUOTE, including escaped one (\")
*/
- private void splitAndPutParamValue(String paramValue) {
+ private void splitAndPutParam(
+ PropertyData propertyData, String paramName, String paramValue) {
// "comma,separated:inside.dquote",pref
// -->
// - comma,separated:inside.dquote
@@ -235,7 +229,7 @@ import java.util.Set;
if (ch == '"') {
if (insideDquote) {
// End of Dquote.
- mInterpreter.propertyParamValue(builder.toString());
+ propertyData.addParam(paramName, builder.toString());
builder = null;
insideDquote = false;
} else {
@@ -248,7 +242,7 @@ import java.util.Set;
// e.g.
// pref,"quoted"
// "quoted",pref
- mInterpreter.propertyParamValue(builder.toString());
+ propertyData.addParam(paramName, builder.toString());
}
}
insideDquote = true;
@@ -258,7 +252,7 @@ import java.util.Set;
Log.w(LOG_TAG, "Comma is used before actual string comes. (" +
paramValue + ")");
} else {
- mInterpreter.propertyParamValue(builder.toString());
+ propertyData.addParam(paramName, builder.toString());
builder = null;
}
} else {
@@ -280,7 +274,7 @@ import java.util.Set;
Log.w(LOG_TAG, "Unintended behavior. We must not see empty StringBuilder " +
"at the end of parameter value parsing.");
} else {
- mInterpreter.propertyParamValue(builder.toString());
+ propertyData.addParam(paramName, builder.toString());
}
}
}
diff --git a/java/com/android/vcard/VCardParser_V21.java b/java/com/android/vcard/VCardParser_V21.java
index 9c43332..5bb37cf 100644
--- a/java/com/android/vcard/VCardParser_V21.java
+++ b/java/com/android/vcard/VCardParser_V21.java
@@ -99,9 +99,9 @@ public final class VCardParser_V21 implements VCardParser {
}
@Override
- public void parse(InputStream is, VCardInterpreter interepreter)
+ public void parse(InputStream is, VCardInterpreter interpreter)
throws IOException, VCardException {
- mVCardParserImpl.parse(is, interepreter);
+ mVCardParserImpl.parse(is, interpreter);
}
@Override
diff --git a/java/com/android/vcard/VCardSourceDetector.java b/java/com/android/vcard/VCardSourceDetector.java
index 28d8a7a..eaff9f2 100644
--- a/java/com/android/vcard/VCardSourceDetector.java
+++ b/java/com/android/vcard/VCardSourceDetector.java
@@ -70,8 +70,8 @@ public class VCardSourceDetector implements VCardInterpreter {
private static final int PARSE_TYPE_APPLE = 1;
// For Japanese mobile phones, which are usually using Shift_JIS as a charset.
private static final int PARSE_TYPE_MOBILE_PHONE_JP = 2;
- // For some of mobile phones released from DoCoMo, which use nested vCard.
- private static final int PARSE_TYPE_DOCOMO_TORELATE_NEST = 3;
+ // For some of mobile phones released from DoCoMo.
+ private static final int PARSE_TYPE_DOCOMO_FOMA = 3;
// For Japanese Windows Mobel phones. It's version is supposed to be 6.5.
private static final int PARSE_TYPE_WINDOWS_MOBILE_V65_JP = 4;
@@ -120,7 +120,7 @@ public class VCardSourceDetector implements VCardInterpreter {
mNeedToParseVersion = true;
return;
} else if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
- mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
+ mParseType = PARSE_TYPE_DOCOMO_FOMA;
// Probably Shift_JIS is used, but we should double confirm.
mNeedToParseCharset = true;
return;
@@ -131,7 +131,7 @@ public class VCardSourceDetector implements VCardInterpreter {
if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) {
mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP;
} else if (FOMA_SIGNS.contains(name)) {
- mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
+ mParseType = PARSE_TYPE_DOCOMO_FOMA;
} else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) {
mParseType = PARSE_TYPE_MOBILE_PHONE_JP;
} else if (APPLE_SIGNS.contains(name)) {
@@ -171,8 +171,8 @@ public class VCardSourceDetector implements VCardInterpreter {
*/
public int getEstimatedType() {
switch (mParseType) {
- case PARSE_TYPE_DOCOMO_TORELATE_NEST:
- return VCardConfig.VCARD_TYPE_DOCOMO | VCardConfig.FLAG_TORELATE_NEST;
+ case PARSE_TYPE_DOCOMO_FOMA:
+ return VCardConfig.VCARD_TYPE_DOCOMO;
case PARSE_TYPE_MOBILE_PHONE_JP:
return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE;
case PARSE_TYPE_APPLE:
@@ -204,7 +204,7 @@ public class VCardSourceDetector implements VCardInterpreter {
}
switch (mParseType) {
case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
- case PARSE_TYPE_DOCOMO_TORELATE_NEST:
+ case PARSE_TYPE_DOCOMO_FOMA:
case PARSE_TYPE_MOBILE_PHONE_JP:
return "SHIFT_JIS";
case PARSE_TYPE_APPLE:
diff --git a/tests/res/raw/v21_nest.vcf b/tests/res/raw/v21_nest.vcf
new file mode 100644
index 0000000..6f29d93
--- /dev/null
+++ b/tests/res/raw/v21_nest.vcf
@@ -0,0 +1,13 @@
+BEGIN:VCARD
+VERSION:2.1
+N:parent
+BEGIN:VCARD
+VERSION:2.1
+N:nest1
+END:VCARD
+BEGIN:VCARD
+VERSION:2.1
+N:nest2
+END:VCARD
+TEL:1
+END:VCARD
diff --git a/tests/src/com/android/vcard/tests/VCardEntryTests.java b/tests/src/com/android/vcard/tests/VCardEntryTests.java
new file mode 100644
index 0000000..4c82dc3
--- /dev/null
+++ b/tests/src/com/android/vcard/tests/VCardEntryTests.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011 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.vcard.tests;
+
+import com.android.vcard.VCardConstants;
+import com.android.vcard.VCardEntry;
+import com.android.vcard.VCardEntryConstructor;
+import com.android.vcard.VCardEntryHandler;
+import com.android.vcard.VCardInterpreter;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.test.AndroidTestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class VCardEntryTests extends AndroidTestCase {
+ private class MockVCardEntryHandler implements VCardEntryHandler {
+ private List<VCardEntry> mEntries = new ArrayList<VCardEntry>();
+ private boolean mOnStartCalled;
+ private boolean mOnEndCalled;
+
+ @Override
+ public void onStart() {
+ assertFalse(mOnStartCalled);
+ mOnStartCalled = true;
+ }
+
+ @Override
+ public void onEntryCreated(VCardEntry entry) {
+ assertTrue(mOnStartCalled);
+ assertFalse(mOnEndCalled);
+ mEntries.add(entry);
+ }
+
+ @Override
+ public void onEnd() {
+ assertTrue(mOnStartCalled);
+ assertFalse(mOnEndCalled);
+ mOnEndCalled = true;
+ }
+
+ public List<VCardEntry> getEntries() {
+ return mEntries;
+ }
+ }
+
+ /**
+ * Tests VCardEntry and related clasess can handle nested classes given
+ * {@link VCardInterpreter} is called appropriately.
+ *
+ * This test manually calls VCardInterpreter's callback mechanism and checks
+ * {@link VCardEntryConstructor} constructs {@link VCardEntry} per given calls.
+ *
+ * Intended vCard is as follows:
+ * <code>
+ * BEGIN:VCARD
+ * N:test1
+ * BEGIN:VCARD
+ * N:test2
+ * END:VCARD
+ * TEL:1
+ * END:VCARD
+ * </code>
+ */
+ public void testNestHandling() {
+ VCardEntryConstructor entryConstructor = new VCardEntryConstructor();
+ MockVCardEntryHandler entryHandler = new MockVCardEntryHandler();
+ entryConstructor.addEntryHandler(entryHandler);
+
+ entryConstructor.start();
+ entryConstructor.startEntry();
+ entryConstructor.startProperty();
+ entryConstructor.propertyName(VCardConstants.PROPERTY_N);
+ entryConstructor.propertyValues(Arrays.asList("test1"));
+ entryConstructor.endProperty();
+
+ entryConstructor.startEntry(); // begin nest
+ entryConstructor.startProperty();
+ entryConstructor.propertyName(VCardConstants.PROPERTY_N);
+ entryConstructor.propertyValues(Arrays.asList("test2"));
+ entryConstructor.endProperty();
+ entryConstructor.endEntry(); // end nest
+
+ entryConstructor.startProperty();
+ entryConstructor.propertyName(VCardConstants.PROPERTY_TEL);
+ entryConstructor.propertyValues(Arrays.asList("1"));
+ entryConstructor.endProperty();
+ entryConstructor.endEntry();
+ entryConstructor.end();
+
+ List<VCardEntry> entries = entryHandler.getEntries();
+ assertEquals(2, entries.size());
+ VCardEntry parent = entries.get(1);
+ VCardEntry child = entries.get(0);
+ assertEquals("test1", parent.getDisplayName());
+ assertEquals("test2", child.getDisplayName());
+ List<VCardEntry.PhoneData> phoneList = parent.getPhoneList();
+ assertNotNull(phoneList);
+ assertEquals(1, phoneList.size());
+ assertEquals("1", phoneList.get(0).data);
+ }
+
+ /**
+ * Tests that VCardEntry emits correct insert operation for name field.
+ */
+ public void testConstructInsertOperationsInsertName() {
+ VCardEntry entry = new VCardEntry();
+ VCardEntry.Property property = new VCardEntry.Property();
+ property.setPropertyName("N");
+ property.addPropertyValue("Family", "Given", "Middle", "Prefix", "Suffix");
+ entry.addProperty(property);
+ entry.consolidateFields();
+
+ assertEquals("Family", entry.getFamilyName());
+ assertEquals("Given", entry.getGivenName());
+ assertEquals("Middle", entry.getMiddleName());
+ assertEquals("Prefix", entry.getPrefix());
+ assertEquals("Suffix", entry.getSuffix());
+
+ ContentResolver resolver = getContext().getContentResolver();
+ ArrayList<ContentProviderOperation> operationList =
+ new ArrayList<ContentProviderOperation>();
+ entry.constructInsertOperations(resolver, operationList);
+
+ // Need too many details for testing these. Just check basics.
+ // TODO: introduce nice-to-test mechanism here.
+ assertEquals(2, operationList.size());
+ assertEquals(ContentProviderOperation.TYPE_INSERT, operationList.get(0).getType());
+ assertEquals(ContentProviderOperation.TYPE_INSERT, operationList.get(1).getType());
+ }
+
+ /**
+ * Tests that VCardEntry refrains from emitting unnecessary insert operation.
+ */
+ public void testConstructInsertOperationsEmptyData() {
+ VCardEntry entry = new VCardEntry();
+ ContentResolver resolver = getContext().getContentResolver();
+ ArrayList<ContentProviderOperation> operationList =
+ new ArrayList<ContentProviderOperation>();
+ entry.constructInsertOperations(resolver, operationList);
+ assertEquals(0, operationList.size());
+ }
+
+ // TODO: add bunch of test for constructInsertOperations..
+} \ No newline at end of file
diff --git a/tests/src/com/android/vcard/tests/VCardImporterNestTests.java b/tests/src/com/android/vcard/tests/VCardImporterNestTests.java
new file mode 100644
index 0000000..f4b04e8
--- /dev/null
+++ b/tests/src/com/android/vcard/tests/VCardImporterNestTests.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 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.vcard.tests;
+
+import com.android.vcard.tests.testutils.VCardTestsBase;
+
+/**
+ * Test cases specific for nested vCard.
+ */
+public class VCardImporterNestTests extends VCardTestsBase {
+ public void testSimpleNest() {
+ mVerifier.initForImportTest(V21, R.raw.v21_nest);
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNodeWithOrder("N", "nest1");
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNodeWithOrder("N", "nest2");
+ // Parent comes last.
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNodeWithOrder("N", "parent")
+ .addExpectedNodeWithOrder("TEL", "1");
+ }
+}
diff --git a/tests/src/com/android/vcard/tests/VCardImporterTests.java b/tests/src/com/android/vcard/tests/VCardImporterTests.java
index 9c677e5..248d015 100644
--- a/tests/src/com/android/vcard/tests/VCardImporterTests.java
+++ b/tests/src/com/android/vcard/tests/VCardImporterTests.java
@@ -18,8 +18,8 @@ package com.android.vcard.tests;
import com.android.vcard.VCardConfig;
import com.android.vcard.tests.testutils.ContentValuesVerifier;
import com.android.vcard.tests.testutils.ContentValuesVerifierElem;
-import com.android.vcard.tests.testutils.VCardTestsBase;
import com.android.vcard.tests.testutils.PropertyNodesVerifierElem.TypeSet;
+import com.android.vcard.tests.testutils.VCardTestsBase;
import android.content.ContentValues;
import android.provider.ContactsContract.CommonDataKinds.Email;
diff --git a/tests/src/com/android/vcard/tests/VCardInterpreterTests.java b/tests/src/com/android/vcard/tests/VCardInterpreterTests.java
new file mode 100644
index 0000000..ad70c5e
--- /dev/null
+++ b/tests/src/com/android/vcard/tests/VCardInterpreterTests.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2011 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.vcard.tests;
+
+import com.android.vcard.VCardInterpreter;
+import com.android.vcard.VCardParser;
+import com.android.vcard.VCardParser_V21;
+import com.android.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.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class VCardInterpreterTests extends AndroidTestCase {
+ private enum Order {
+ START,
+ END,
+ START_ENTRY,
+ END_ENTRY,
+ START_PROPERTY,
+ END_PROPERTY,
+ PROPERTY_GROUP,
+ PROPERTY_NAME,
+ PROPERTY_PARAM_TYPE,
+ PROPERTY_PARAM_VALUE,
+ PROPERTY_VALUES
+ }
+
+ private class MockVCardInterpreter implements VCardInterpreter {
+ private final List<String> mHistory = new ArrayList<String>();
+ private final List<Object> mExpectedOrder = new ArrayList<Object>();
+
+ public MockVCardInterpreter addExpectedOrder(Order order) {
+ mExpectedOrder.add(order);
+ return this;
+ }
+
+ public MockVCardInterpreter addExpectedOrderGroup(Order... orders) {
+ mExpectedOrder.add(new HashSet<Order>(Arrays.asList(orders)));
+ return this;
+ }
+
+ private void inspectOrder(Order order) {
+ mHistory.add(order.toString());
+ final Object top = mExpectedOrder.get(0);
+ if (top instanceof Set) {
+ @SuppressWarnings("unchecked")
+ Set<Order> orderSet = (Set<Order>)top;
+ assertTrue(String.format("Unexpected order: %s",
+ Arrays.toString(mHistory.toArray(new String[0]))),
+ orderSet.remove(order));
+ if (orderSet.isEmpty()) {
+ mExpectedOrder.remove(0);
+ }
+ } else {
+ assertEquals(top, order);
+ mExpectedOrder.remove(0);
+ }
+ }
+
+ public void verify() {
+ assertTrue(String.format("Remaining: " + Arrays.toString(mExpectedOrder.toArray())),
+ mExpectedOrder.isEmpty());
+ }
+
+ @Override
+ public void start() {
+ inspectOrder(Order.START);
+ }
+
+ @Override
+ public void end() {
+ inspectOrder(Order.END);
+ }
+
+ @Override
+ public void startEntry() {
+ inspectOrder(Order.START_ENTRY);
+ }
+
+ @Override
+ public void endEntry() {
+ inspectOrder(Order.END_ENTRY);
+ }
+
+ @Override
+ public void startProperty() {
+ inspectOrder(Order.START_PROPERTY);
+ }
+
+ @Override
+ public void endProperty() {
+ inspectOrder(Order.END_PROPERTY);
+ }
+
+ @Override
+ public void propertyGroup(String group) {
+ inspectOrder(Order.PROPERTY_GROUP);
+ }
+
+ @Override
+ public void propertyName(String name) {
+ inspectOrder(Order.PROPERTY_NAME);
+ }
+
+ @Override
+ public void propertyParamType(String type) {
+ inspectOrder(Order.PROPERTY_PARAM_TYPE);
+ }
+
+ @Override
+ public void propertyParamValue(String value) {
+ inspectOrder(Order.PROPERTY_PARAM_VALUE);
+ }
+
+ @Override
+ public void propertyValues(List<String> values) {
+ inspectOrder(Order.PROPERTY_VALUES);
+ }
+ }
+
+ public void testSimple() throws IOException, VCardException {
+ InputStream inputStream = getContext().getResources().openRawResource(R.raw.v21_simple_1);
+ VCardParser parser = new VCardParser_V21();
+ MockVCardInterpreter interpreter = new MockVCardInterpreter();
+ interpreter.addExpectedOrder(Order.START)
+ .addExpectedOrder(Order.START_ENTRY)
+ .addExpectedOrder(Order.START_PROPERTY)
+ .addExpectedOrderGroup(Order.PROPERTY_NAME, Order.PROPERTY_VALUES)
+ .addExpectedOrder(Order.END_PROPERTY)
+ .addExpectedOrder(Order.END_ENTRY)
+ .addExpectedOrder(Order.END);
+ parser.parse(inputStream, interpreter);
+ interpreter.verify();
+ }
+
+ public void testNest() throws IOException, VCardException {
+ InputStream inputStream = getContext().getResources().openRawResource(R.raw.v21_nest);
+ VCardParser parser = new VCardParser_V21();
+ MockVCardInterpreter interpreter = new MockVCardInterpreter();
+ interpreter.addExpectedOrder(Order.START)
+ .addExpectedOrder(Order.START_ENTRY)
+ .addExpectedOrder(Order.START_PROPERTY) // For VERSION
+ .addExpectedOrderGroup(Order.PROPERTY_NAME, Order.PROPERTY_VALUES)
+ .addExpectedOrder(Order.END_PROPERTY)
+ .addExpectedOrder(Order.START_PROPERTY) // For N
+ .addExpectedOrderGroup(Order.PROPERTY_NAME, Order.PROPERTY_VALUES)
+ .addExpectedOrder(Order.END_PROPERTY)
+ .addExpectedOrder(Order.START_ENTRY) // First nested vCard begins
+ .addExpectedOrder(Order.START_PROPERTY) // For VERSION
+ .addExpectedOrderGroup(Order.PROPERTY_NAME, Order.PROPERTY_VALUES)
+ .addExpectedOrder(Order.END_PROPERTY)
+ .addExpectedOrder(Order.START_PROPERTY) // For N
+ .addExpectedOrderGroup(Order.PROPERTY_NAME, Order.PROPERTY_VALUES)
+ .addExpectedOrder(Order.END_PROPERTY)
+ .addExpectedOrder(Order.END_ENTRY) // First nested vCard ends
+ .addExpectedOrder(Order.START_ENTRY) // Second nested vCard begins
+ .addExpectedOrder(Order.START_PROPERTY) // For VERSION
+ .addExpectedOrderGroup(Order.PROPERTY_NAME, Order.PROPERTY_VALUES)
+ .addExpectedOrder(Order.END_PROPERTY)
+ .addExpectedOrder(Order.START_PROPERTY) // For N
+ .addExpectedOrderGroup(Order.PROPERTY_NAME, Order.PROPERTY_VALUES)
+ .addExpectedOrder(Order.END_PROPERTY)
+ .addExpectedOrder(Order.END_ENTRY) // Second nested vCard ends
+ .addExpectedOrder(Order.START_PROPERTY) // For TEL
+ .addExpectedOrderGroup(Order.PROPERTY_NAME, Order.PROPERTY_VALUES)
+ .addExpectedOrder(Order.END_PROPERTY)
+ .addExpectedOrder(Order.END_ENTRY)
+ .addExpectedOrder(Order.END);
+ parser.parse(inputStream, interpreter);
+ interpreter.verify();
+ }
+} \ No newline at end of file
diff --git a/tests/src/com/android/vcard/tests/VCardTestRunner.java b/tests/src/com/android/vcard/tests/VCardTestRunner.java
index a1f594a..2409339 100644
--- a/tests/src/com/android/vcard/tests/VCardTestRunner.java
+++ b/tests/src/com/android/vcard/tests/VCardTestRunner.java
@@ -27,11 +27,14 @@ public class VCardTestRunner extends InstrumentationTestRunner {
@Override
public TestSuite getAllTests() {
TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(VCardEntryTests.class);
+ suite.addTestSuite(VCardInterpreterTests.class);
suite.addTestSuite(VCardUtilsTests.class);
suite.addTestSuite(VCardTestUtilsTests.class);
suite.addTestSuite(VCardImporterTests.class);
suite.addTestSuite(VCardExporterTests.class);
suite.addTestSuite(VCardJapanizationTests.class);
+ suite.addTestSuite(VCardImporterNestTests.class);
return suite;
}
diff --git a/tests/src/com/android/vcard/tests/testutils/ContentValuesVerifierElem.java b/tests/src/com/android/vcard/tests/testutils/ContentValuesVerifierElem.java
index 86f2a23..38bc14f 100644
--- a/tests/src/com/android/vcard/tests/testutils/ContentValuesVerifierElem.java
+++ b/tests/src/com/android/vcard/tests/testutils/ContentValuesVerifierElem.java
@@ -21,22 +21,13 @@ import android.test.AndroidTestCase;
import com.android.vcard.VCardEntry;
import com.android.vcard.VCardEntryCommitter;
-import com.android.vcard.VCardEntryConstructor;
import com.android.vcard.VCardEntryHandler;
-import com.android.vcard.VCardParser;
-import com.android.vcard.VCardUtils;
-import com.android.vcard.exception.VCardException;
-
-import java.io.IOException;
-import java.io.InputStream;
public class ContentValuesVerifierElem {
- private final AndroidTestCase mTestCase;
private final ImportTestResolver mResolver;
private final VCardEntryHandler mHandler;
public ContentValuesVerifierElem(AndroidTestCase androidTestCase) {
- mTestCase = androidTestCase;
mResolver = new ImportTestResolver(androidTestCase);
mHandler = new VCardEntryCommitter(mResolver);
}
@@ -48,28 +39,6 @@ public class ContentValuesVerifierElem {
return new ContentValuesBuilder(contentValues);
}
- public void verify(int resId, int vcardType)
- throws IOException, VCardException {
- verify(mTestCase.getContext().getResources().openRawResource(resId), vcardType);
- }
-
- public void verify(InputStream is, int vcardType) throws IOException, VCardException {
- final VCardParser vCardParser = VCardUtils.getAppropriateParser(vcardType);
- final VCardEntryConstructor builder = new VCardEntryConstructor(vcardType, null);
- builder.addEntryHandler(mHandler);
- try {
- vCardParser.parse(is, builder);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- }
- }
- }
- verifyResolver();
- }
-
public void verifyResolver() {
mResolver.verify();
}
diff --git a/tests/src/com/android/vcard/tests/testutils/ImportTestProvider.java b/tests/src/com/android/vcard/tests/testutils/ImportTestProvider.java
index eea6cda..8ef0009 100644
--- a/tests/src/com/android/vcard/tests/testutils/ImportTestProvider.java
+++ b/tests/src/com/android/vcard/tests/testutils/ImportTestProvider.java
@@ -190,6 +190,9 @@ public class ImportTestProvider extends MockContentProvider {
return fakeResultArray;
}
+ /**
+ * Checks all expected ContentValues are consumed during import.
+ */
public void verify() {
StringBuilder builder = new StringBuilder();
for (Collection<ContentValues> contentValuesCollection :
diff --git a/tests/src/com/android/vcard/tests/testutils/PropertyNodesVerifier.java b/tests/src/com/android/vcard/tests/testutils/PropertyNodesVerifier.java
index f5dccd0..f9d75ce 100644
--- a/tests/src/com/android/vcard/tests/testutils/PropertyNodesVerifier.java
+++ b/tests/src/com/android/vcard/tests/testutils/PropertyNodesVerifier.java
@@ -73,11 +73,15 @@ public class PropertyNodesVerifier extends VNodeBuilder {
}
@Override
+ public void startEntry() {
+ super.startEntry();
+ AndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size());
+ }
+
+ @Override
public void endEntry() {
+ mPropertyNodesVerifierElemList.get(mIndex).verify(getCurrentVNode());
super.endEntry();
- AndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size());
- AndroidTestCase.assertTrue(mIndex < vNodeList.size());
- mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex));
mIndex++;
}
}
diff --git a/tests/src/com/android/vcard/tests/testutils/VCardVerifier.java b/tests/src/com/android/vcard/tests/testutils/VCardVerifier.java
index a145ebd..9d7666f 100644
--- a/tests/src/com/android/vcard/tests/testutils/VCardVerifier.java
+++ b/tests/src/com/android/vcard/tests/testutils/VCardVerifier.java
@@ -258,6 +258,7 @@ public class VCardVerifier {
final VCardParser parser = VCardUtils.getAppropriateParser(mVCardType);
parser.parse(is, interpreter);
} catch (VCardException e) {
+ Log.e(LOG_TAG, "VCardException", e);
AndroidTestCase.fail("Unexpected VCardException: " + e.getMessage());
}
}
diff --git a/tests/src/com/android/vcard/tests/testutils/VNode.java b/tests/src/com/android/vcard/tests/testutils/VNode.java
index 9bf5426..f16d155 100644
--- a/tests/src/com/android/vcard/tests/testutils/VNode.java
+++ b/tests/src/com/android/vcard/tests/testutils/VNode.java
@@ -21,10 +21,5 @@ import java.util.ArrayList;
* Previously used in main vCard handling code but now exists only 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/src/com/android/vcard/tests/testutils/VNodeBuilder.java b/tests/src/com/android/vcard/tests/testutils/VNodeBuilder.java
index b10c96d..460d440 100644
--- a/tests/src/com/android/vcard/tests/testutils/VNodeBuilder.java
+++ b/tests/src/com/android/vcard/tests/testutils/VNodeBuilder.java
@@ -41,10 +41,9 @@ import java.util.List;
* </p>
*/
public class VNodeBuilder implements VCardInterpreter {
- static private String LOG_TAG = "VNodeBuilder";
-
- public List<VNode> vNodeList = new ArrayList<VNode>();
- private int mNodeListPos = 0;
+ private static String LOG_TAG = "VNodeBuilder";
+
+ private List<VNode> mVNodeList = new ArrayList<VNode>();
private VNode mCurrentVNode;
private PropertyNode mCurrentPropNode;
private String mCurrentParamType;
@@ -58,9 +57,9 @@ public class VNodeBuilder implements VCardInterpreter {
* The charset with which byte array is encoded to String.
*/
private String mTargetCharset;
-
+
private boolean mStrictLineBreakParsing;
-
+
public VNodeBuilder() {
this(VCardConfig.DEFAULT_IMPORT_CHARSET, false);
}
@@ -75,70 +74,53 @@ public class VNodeBuilder implements VCardInterpreter {
mStrictLineBreakParsing = strictLineBreakParsing;
}
+ @Override
public void start() {
}
+ @Override
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.
+ @Override
public void startEntry() {
- VNode vnode = new VNode();
- vnode.parseStatus = 1;
- vnode.VName = "VCARD";
- // 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);
+ mCurrentVNode = new VNode();
+ mVNodeList.add(mCurrentVNode);
}
+ @Override
public void endEntry() {
- VNode endNode = vNodeList.get(mNodeListPos);
- endNode.parseStatus = 0;
- while(mNodeListPos > 0){
- mNodeListPos--;
- if((vNodeList.get(mNodeListPos)).parseStatus == 1)
- break;
- }
- mCurrentVNode = vNodeList.get(mNodeListPos);
+ int lastIndex = mVNodeList.size() - 1;
+ mVNodeList.remove(lastIndex--);
+ mCurrentVNode = lastIndex >= 0 ? mVNodeList.get(lastIndex) : null;
}
+ @Override
public void startProperty() {
mCurrentPropNode = new PropertyNode();
}
+ @Override
public void endProperty() {
mCurrentVNode.propList.add(mCurrentPropNode);
}
-
+
+ @Override
public void propertyName(String name) {
mCurrentPropNode.propName = name;
}
+ @Override
public void propertyGroup(String group) {
mCurrentPropNode.propGroupSet.add(group);
}
-
+
+ @Override
public void propertyParamType(String type) {
mCurrentParamType = type;
}
+ @Override
public void propertyParamValue(String value) {
if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
value = VCardUtils.convertStringCharset(value,
@@ -173,7 +155,7 @@ public class VNodeBuilder implements VCardInterpreter {
return null;
}
}
-
+
private String handleOneValue(String value, String targetCharset, String encoding) {
if (encoding != null) {
encoding = encoding.toUpperCase();
@@ -189,7 +171,8 @@ public class VNodeBuilder implements VCardInterpreter {
}
return encodeString(value, targetCharset);
}
-
+
+ @Override
public void propertyValues(List<String> values) {
if (values == null || values.size() == 0) {
mCurrentPropNode.propValue_bytes = null;
@@ -198,16 +181,16 @@ public class VNodeBuilder implements VCardInterpreter {
mCurrentPropNode.propValue = "";
return;
}
-
+
ContentValues paramMap = mCurrentPropNode.paramMap;
-
- String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET"));
- String encoding = paramMap.getAsString("ENCODING");
-
+
+ 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));
@@ -234,8 +217,16 @@ public class VNodeBuilder implements VCardInterpreter {
return "";
}
}
-
+
public String getResult(){
throw new RuntimeException("Not supported");
}
+
+ public List<VNode> getVNodeList() {
+ return mVNodeList;
+ }
+
+ public VNode getCurrentVNode() {
+ return mCurrentVNode;
+ }
}