From 4ac671ad970a5da7201b23d683651f8d3fd49be6 Mon Sep 17 00:00:00 2001
From: Daisuke Miyakawa
- * The constructor which uses the estimated type available from a given
- * detector.
+ * The constructor which uses the estimated type available from a given detector.
*
+ * 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.
+ *
+ * Basic implementation achieving vCard 3.0 parsing.
+ *
+ * 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}.
+ * vcard_file = [wsls] vcard [wsls]
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.
+ *
* 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. *
* 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+ * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 2.1. + *
+ ** 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. + *
*/ - // TODO: move B to another something and make this member public - /* package */ static final HashSet+ * vCard parser for vCard 3.0. See RFC 2426 for more detail. + *
+ *+ * 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. + *
+ *+ * 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". + *
*/ public class VCardParser_V30 implements VCardParser { - public static final Set+ * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 3.0. + *
+ *+ * Though vCard 2.1 specification does not allow "7BIT" or "BASE64", we allow them for safety. + *
+ *+ * "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. + *
+ */ + /* package */ static final Set