diff options
| author | Daisuke Miyakawa <dmiyakawa@google.com> | 2010-04-09 14:49:04 +0900 |
|---|---|---|
| committer | Daisuke Miyakawa <dmiyakawa@google.com> | 2010-04-09 14:53:24 +0900 |
| commit | 4ac671ad970a5da7201b23d683651f8d3fd49be6 (patch) | |
| tree | 1fd6717b7a4a12f69c7547fc4dbab4bdeca77ce9 /core/java/android/pim | |
| parent | 182a2f380f41e0f6fd0d228a7db56fdf83694fac (diff) | |
| download | frameworks_base-4ac671ad970a5da7201b23d683651f8d3fd49be6.zip frameworks_base-4ac671ad970a5da7201b23d683651f8d3fd49be6.tar.gz frameworks_base-4ac671ad970a5da7201b23d683651f8d3fd49be6.tar.bz2 | |
Add comments for vCard parser.
Bug: 2576594
Change-Id: I3303a350bdea7b1b84bd1fb318d31ba5a229457d
Diffstat (limited to 'core/java/android/pim')
| -rw-r--r-- | core/java/android/pim/vcard/VCardBuilder.java | 10 | ||||
| -rw-r--r-- | core/java/android/pim/vcard/VCardConstants.java | 16 | ||||
| -rw-r--r-- | core/java/android/pim/vcard/VCardParserImpl_V21.java | 129 | ||||
| -rw-r--r-- | core/java/android/pim/vcard/VCardParserImpl_V30.java | 16 | ||||
| -rw-r--r-- | core/java/android/pim/vcard/VCardParser_V21.java | 33 | ||||
| -rw-r--r-- | core/java/android/pim/vcard/VCardParser_V30.java | 39 |
6 files changed, 144 insertions, 99 deletions
diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java index fa0d4d4..9b8cfbb 100644 --- a/core/java/android/pim/vcard/VCardBuilder.java +++ b/core/java/android/pim/vcard/VCardBuilder.java @@ -91,10 +91,12 @@ public class VCardBuilder { private static final String VCARD_WS = " "; private static final String VCARD_PARAM_EQUAL = "="; - private static final String VCARD_PARAM_ENCODING_QP = "ENCODING=QUOTED-PRINTABLE"; - - private static final String VCARD_PARAM_ENCODING_BASE64_V21 = "ENCODING=BASE64"; - private static final String VCARD_PARAM_ENCODING_BASE64_V30 = "ENCODING=b"; + private static final String VCARD_PARAM_ENCODING_QP = + "ENCODING=" + VCardConstants.PARAM_ENCODING_QP; + private static final String VCARD_PARAM_ENCODING_BASE64_V21 = + "ENCODING=" + VCardConstants.PARAM_ENCODING_BASE64; + private static final String VCARD_PARAM_ENCODING_BASE64_V30 = + "ENCODING=" + VCardConstants.PARAM_ENCODING_B; private static final String SHIFT_JIS = "SHIFT_JIS"; diff --git a/core/java/android/pim/vcard/VCardConstants.java b/core/java/android/pim/vcard/VCardConstants.java index 8c07126..e11b1fd 100644 --- a/core/java/android/pim/vcard/VCardConstants.java +++ b/core/java/android/pim/vcard/VCardConstants.java @@ -109,6 +109,12 @@ public class VCardConstants { public static final String PARAM_TYPE_BBS = "BBS"; public static final String PARAM_TYPE_VIDEO = "VIDEO"; + public static final String PARAM_ENCODING_7BIT = "7BIT"; + public static final String PARAM_ENCODING_8BIT = "8BIT"; + public static final String PARAM_ENCODING_QP = "QUOTED-PRINTABLE"; + public static final String PARAM_ENCODING_BASE64 = "BASE64"; // Available in vCard 2.1 + public static final String PARAM_ENCODING_B = "B"; // Available in vCard 3.0 + // TYPE parameters for Phones, which are not formally valid in vCard (at least 2.1). // These types are basically encoded to "X-" parameters when composing vCard. // Parser passes these when "X-" is added to the parameter or not. @@ -130,10 +136,6 @@ public class VCardConstants { // Do not use in composer side. public static final String PARAM_EXTRA_TYPE_COMPANY = "COMPANY"; - // DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of SORT-STRING in - // vCard 3.0. - public static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N"; - public interface ImportOnly { public static final String PROPERTY_X_NICKNAME = "X-NICKNAME"; // Some device emits this "X-" parameter for expressing Google Talk, @@ -142,6 +144,12 @@ public class VCardConstants { public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK"; } + //// Mainly for package constants. + + // DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of + // SORT-STRING invCard 3.0. + /* package */ static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N"; + /* package */ static final int MAX_DATA_COLUMN = 15; /* package */ static final int MAX_CHARACTER_NUMS_QP = 76; diff --git a/core/java/android/pim/vcard/VCardParserImpl_V21.java b/core/java/android/pim/vcard/VCardParserImpl_V21.java index c3b725f..22fdb75 100644 --- a/core/java/android/pim/vcard/VCardParserImpl_V21.java +++ b/core/java/android/pim/vcard/VCardParserImpl_V21.java @@ -63,10 +63,7 @@ import java.util.Set; private static final String sDefaultEncoding = "8BIT"; - //// Protected members - protected boolean mCanceled; - protected VCardInterpreter mInterpreter; /** @@ -109,7 +106,6 @@ import java.util.Set; */ protected final Set<String> mUnknownValueSet = new HashSet<String>(); - // // Private members // In some cases, vCard is nested. Currently, we only consider the most // interior vCard data. @@ -121,27 +117,16 @@ import java.util.Set; // 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; /** @@ -156,8 +141,7 @@ import java.util.Set; /** * <p> - * The constructor which uses the estimated type available from a given - * detector. + * The constructor which uses the estimated type available from a given detector. * </p> */ public VCardParserImpl_V21(VCardSourceDetector detector) { @@ -186,15 +170,15 @@ import java.util.Set; */ // <pre class="prettyprint">vcard_file = [wsls] vcard [wsls]</pre> protected void parseVCardFile() throws IOException, VCardException { - boolean firstReading = true; + boolean firstRead = true; while (true) { if (mCanceled) { break; } - if (!parseOneVCard(firstReading)) { + if (!parseOneVCard(firstRead)) { break; } - firstReading = false; + firstRead = false; } if (mNestCount > 0) { @@ -245,12 +229,13 @@ import java.util.Set; } /* - * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] - * ":" [ws] "VCARD" + * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF + * items *CRLF + * "END" [ws] ":" [ws] "VCARD" */ - private boolean parseOneVCard(boolean firstReading) throws IOException, VCardException { + private boolean parseOneVCard(boolean firstRead) throws IOException, VCardException { boolean allowGarbage = false; - if (firstReading) { + if (firstRead) { if (mNestCount > 0) { for (int i = 0; i < mNestCount; i++) { if (!readBeginVCard(allowGarbage)) { @@ -456,34 +441,29 @@ import java.util.Set; throw new VCardException("Unknown property name: \"" + propertyName + "\""); } - static private final int STATE_GROUP_OR_PROPNAME = 0; - + // For performance reason, the states for group and property name are merged into one. + static private final int STATE_GROUP_OR_PROPERTY_NAME = 0; static private final int STATE_PARAMS = 1; - - // vCard 3.0 specification allows double-quoted param-value, while vCard 2.1 - // does not. - // This is just for safety. + // 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 { - int state = STATE_GROUP_OR_PROPNAME; - int nameIndex = 0; - final String[] propertyNameAndValue = new String[2]; - final int length = line.length(); if (length > 0 && line.charAt(0) == '#') { throw new VCardInvalidCommentLineException(); } - // This loop is developed so that we don't have to take care of bottle - // neck here. + int state = STATE_GROUP_OR_PROPERTY_NAME; + int nameIndex = 0; + + // This loop is developed so that we don't have to take care of bottle neck here. // Refactor carefully when you need to do so. for (int i = 0; i < length; i++) { final char ch = line.charAt(i); switch (state) { - case STATE_GROUP_OR_PROPNAME: { - if (ch == ':') { + 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; @@ -499,14 +479,16 @@ import java.util.Set; propertyNameAndValue[1] = ""; } return propertyNameAndValue; - } else if (ch == '.') { - String groupName = line.substring(nameIndex, i); - if (mInterpreter != null) { + } 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 if (mInterpreter != null) { mInterpreter.propertyGroup(groupName); } - nameIndex = i + 1; - } else if (ch == ';') { - String propertyName = line.substring(nameIndex, i); + 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; @@ -516,17 +498,21 @@ import java.util.Set; } propertyNameAndValue[0] = propertyName; nameIndex = i + 1; - state = STATE_PARAMS; + state = STATE_PARAMS; // Start parameter parsing. } break; } case STATE_PARAMS: { if (ch == '"') { + if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) { + Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " + + "Silently allow it"); + } state = STATE_PARAMS_IN_DQUOTE; - } else if (ch == ';') { + } else if (ch == ';') { // Starts another param. handleParams(line.substring(nameIndex, i)); nameIndex = i + 1; - } else if (ch == ':') { + } 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); @@ -539,6 +525,10 @@ import java.util.Set; } case STATE_PARAMS_IN_DQUOTE: { if (ch == '"') { + if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) { + Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " + + "Silently allow it"); + } state = STATE_PARAMS; } break; @@ -557,7 +547,7 @@ import java.util.Set; * [ws] word / knowntype */ protected void handleParams(String params) throws VCardException { - String[] strArray = params.split("=", 2); + final String[] strArray = params.split("=", 2); if (strArray.length == 2) { final String paramName = strArray[0].trim().toUpperCase(); String paramValue = strArray[1].trim(); @@ -582,7 +572,7 @@ import java.util.Set; } /** - * vCard 3.0 parser may throw VCardException. + * vCard 3.0 parser implementation may throw VCardException. */ @SuppressWarnings("unused") protected void handleParamWithoutName(final String paramValue) throws VCardException { @@ -593,15 +583,15 @@ import java.util.Set; * ptypeval = knowntype / "X-" word */ protected void handleType(final String ptypeval) { - String upperTypeValue = ptypeval; - if (!(getKnownTypeSet().contains(upperTypeValue) || upperTypeValue.startsWith("X-")) + 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)); } if (mInterpreter != null) { mInterpreter.propertyParamType("TYPE"); - mInterpreter.propertyParamValue(upperTypeValue); + mInterpreter.propertyParamValue(ptypeval); } } @@ -609,10 +599,12 @@ import java.util.Set; * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word */ protected void handleValue(final String pvalueval) { - if (!getKnownValueSet().contains(pvalueval.toUpperCase()) && pvalueval.startsWith("X-") - && !mUnknownValueSet.contains(pvalueval)) { + if (!(getKnownValueSet().contains(pvalueval.toUpperCase()) + || pvalueval.startsWith("X-") + || mUnknownValueSet.contains(pvalueval))) { mUnknownValueSet.add(pvalueval); - Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), pvalueval)); + Log.w(LOG_TAG, String.format( + "The value unsupported by TYPE of %s: ", getVersion(), pvalueval)); } if (mInterpreter != null) { mInterpreter.propertyParamType("VALUE"); @@ -621,8 +613,7 @@ import java.util.Set; } /* - * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" - * word + * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word */ protected void handleEncoding(String pencodingval) throws VCardException { if (getAvailableEncodingSet().contains(pencodingval) || @@ -638,8 +629,11 @@ import java.util.Set; } /** - * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC - * 1521), but today's vCard often contains other charset, so we allow them. + * <p> + * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521), + * but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc. + * We allow any charset. + * </p> */ protected void handleCharset(String charsetval) { if (mInterpreter != null) { @@ -695,7 +689,8 @@ import java.util.Set; protected void handlePropertyValue(String propertyName, String propertyValue) throws IOException, VCardException { - if (mCurrentEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { + final String upperEncoding = mCurrentEncoding.toUpperCase(); + if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) { final long start = System.currentTimeMillis(); final String result = getQuotedPrintable(propertyValue); if (mInterpreter != null) { @@ -704,8 +699,8 @@ import java.util.Set; mInterpreter.propertyValues(v); } mTimeHandleQuotedPrintable += System.currentTimeMillis() - start; - } else if (mCurrentEncoding.equalsIgnoreCase("BASE64") - || mCurrentEncoding.equalsIgnoreCase("B")) { + } 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. @@ -724,11 +719,11 @@ import java.util.Set; } mTimeHandleBase64 += System.currentTimeMillis() - start; } else { - if (!(mCurrentEncoding == null || mCurrentEncoding.equalsIgnoreCase("7BIT") - || mCurrentEncoding.equalsIgnoreCase("8BIT") || mCurrentEncoding.toUpperCase() - .startsWith("X-"))) { - Log.w(LOG_TAG, "The encoding unsupported by vCard spec: \"" + mCurrentEncoding - + "\"."); + if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") || + upperEncoding.startsWith("X-"))) { + Log.w(LOG_TAG, + String.format("The encoding \"%s\" is unsupported by vCard %s", + mCurrentEncoding, getVersionString())); } final long start = System.currentTimeMillis(); diff --git a/core/java/android/pim/vcard/VCardParserImpl_V30.java b/core/java/android/pim/vcard/VCardParserImpl_V30.java index 5257cde..fb6ee4f 100644 --- a/core/java/android/pim/vcard/VCardParserImpl_V30.java +++ b/core/java/android/pim/vcard/VCardParserImpl_V30.java @@ -21,11 +21,22 @@ import java.util.Set; import android.pim.vcard.exception.VCardException; import android.util.Log; +/** + * <p> + * Basic implementation achieving vCard 3.0 parsing. + * </p> + * <p> + * This class inherits vCard 2.1 implementation since technically they are similar, + * while specifically there's logical no relevance between them. + * So that developers are not confused with the inheritance, + * {@link VCardParser_V30} does not inherit {@link VCardParser_V21}, while + * {@link VCardParserImpl_V30} inherits {@link VCardParserImpl_V21}. + * </p> + */ /* package */ class VCardParserImpl_V30 extends VCardParserImpl_V21 { private static final String LOG_TAG = "VCardParserImpl_V30"; private String mPreviousLine; - private boolean mEmittedAgentWarning = false; public VCardParserImpl_V30() { @@ -239,8 +250,7 @@ import android.util.Log; while (true) { final String line = getLine(); if (line == null) { - throw new VCardException( - "File ended during parsing BASE64 binary"); + throw new VCardException("File ended during parsing BASE64 binary"); } if (line.length() == 0) { break; diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java index d29f60a..f7e496a 100644 --- a/core/java/android/pim/vcard/VCardParser_V21.java +++ b/core/java/android/pim/vcard/VCardParser_V21.java @@ -26,14 +26,13 @@ import java.util.Set; /** * </p> - * vCard parser implementation mostly for vCard 2.1. See the specification for more detail - * about the spec itself. + * vCard parser for vCard 2.1. See the specification for more detail about the spec itself. * </p> * <p> * The spec is written in 1996, and currently various types of "vCard 2.1" exist. * To handle real the world vCard formats appropriately and effectively, this class does not - * obey with strict vCard 2.1. In stead, not only vCard spec but also real world - * vCard is considered. + * obey with strict vCard 2.1. + * In stead, not only vCard spec but also real world vCard is considered. * </p> * e.g. A lot of devices and softwares let vCard importer/exporter to use * the PNG format to determine the type of image, while it is not allowed in @@ -45,7 +44,7 @@ public final class VCardParser_V21 implements VCardParser { /** * A unmodifiable Set storing the property names available in the vCard 2.1 specification. */ - public static final Set<String> sKnownPropertyNameSet = + /* package */ static final Set<String> sKnownPropertyNameSet = Collections.unmodifiableSet(new HashSet<String>( Arrays.asList("BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", @@ -54,7 +53,7 @@ public final class VCardParser_V21 implements VCardParser { /** * A unmodifiable Set storing the types known in vCard 2.1. */ - public static final Set<String> sKnownTypeSet = + /* package */ static final Set<String> sKnownTypeSet = Collections.unmodifiableSet(new HashSet<String>( Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK", "PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS", @@ -66,20 +65,28 @@ public final class VCardParser_V21 implements VCardParser { "WAVE", "AIFF", "PCM", "X509", "PGP"))); /** - * A unmodifiable Set storing the values available in the vCard 2.1 specification. + * A unmodifiable Set storing the values for the type "VALUE", available in the vCard 2.1. */ - public static final Set<String> sKnownValueSet = + /* package */ static final Set<String> sKnownValueSet = Collections.unmodifiableSet(new HashSet<String>( Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID"))); /** + * <p> + * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 2.1. + * </p> + * <p> * Though vCard 2.1 specification does not allow "B" encoding, some data may have it. - * We allow it for safety... + * We allow it for safety. + * </p> */ - // TODO: move B to another something and make this member public - /* package */ static final HashSet<String> sAvailableEncoding = - new HashSet<String>(Arrays.asList( - "7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B")); + /* package */ static final Set<String> sAvailableEncoding = + Collections.unmodifiableSet(new HashSet<String>( + Arrays.asList(VCardConstants.PARAM_ENCODING_7BIT, + VCardConstants.PARAM_ENCODING_8BIT, + VCardConstants.PARAM_ENCODING_QP, + VCardConstants.PARAM_ENCODING_BASE64, + VCardConstants.PARAM_ENCODING_B))); private final VCardParserImpl_V21 mVCardParserImpl; diff --git a/core/java/android/pim/vcard/VCardParser_V30.java b/core/java/android/pim/vcard/VCardParser_V30.java index ec97f19..13b4196 100644 --- a/core/java/android/pim/vcard/VCardParser_V30.java +++ b/core/java/android/pim/vcard/VCardParser_V30.java @@ -25,22 +25,46 @@ import java.util.HashSet; import java.util.Set; /** - * The class used to parse vCard 3.0. - * Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426). + * <p> + * vCard parser for vCard 3.0. See RFC 2426 for more detail. + * </p> + * <p> + * This parser allows vCard format which is not allowed in the RFC, since + * we have seen several vCard 3.0 files which don't comply with it. + * </p> + * <p> + * e.g. vCard 3.0 does not allow "CHARSET" attribute, but some actual files + * have it and they uses non UTF-8 charsets. UTF-8 is recommended in RFC 2426, + * but it is not a must. We silently allow "CHARSET". + * </p> */ public class VCardParser_V30 implements VCardParser { - public static final Set<String> sKnownPropertyNameSet = + /* package */ static final Set<String> sKnownPropertyNameSet = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList( "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1 "NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS", "SORT-STRING", "CATEGORIES", "PRODID"))); // 3.0 - - // Although "7bit" and "BASE64" is not allowed in vCard 3.0, we allow it for safety. - public static final Set<String> sAcceptableEncoding = + + /** + * <p> + * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 3.0. + * </p> + * <p> + * Though vCard 2.1 specification does not allow "7BIT" or "BASE64", we allow them for safety. + * </p> + * <p> + * "QUOTED-PRINTABLE" is not allowed in vCard 3.0 and not in this parser either, + * because the encoding ambiguates how the vCard file to be parsed. + * </p> + */ + /* package */ static final Set<String> sAcceptableEncoding = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList( - "7BIT", "8BIT", "BASE64", "B"))); + VCardConstants.PARAM_ENCODING_7BIT, + VCardConstants.PARAM_ENCODING_8BIT, + VCardConstants.PARAM_ENCODING_BASE64, + VCardConstants.PARAM_ENCODING_B))); private final VCardParserImpl_V30 mVCardParserImpl; @@ -56,7 +80,6 @@ public class VCardParser_V30 implements VCardParser { mVCardParserImpl = new VCardParserImpl_V30(parseType); } - //// Implemented methods public boolean parse(InputStream is, VCardInterpreter interepreter) |
