14199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/* 24199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Copyright (C) 2010 The Android Open Source Project 34199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 44199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License"); 54199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * you may not use this file except in compliance with the License. 64199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * You may obtain a copy of the License at 74199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 84199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * http://www.apache.org/licenses/LICENSE-2.0 94199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software 114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS, 124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * See the License for the specific language governing permissions and 144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * limitations under the License. 154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapackage com.android.vcard; 174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 18a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Chengimport android.text.TextUtils; 19a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Chengimport android.util.Base64; 20a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Chengimport android.util.Log; 21a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng 224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardAgentNotSupportedException; 234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardException; 244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardInvalidCommentLineException; 254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardInvalidLineException; 264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardVersionException; 274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.BufferedReader; 294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.IOException; 304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.InputStream; 314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.InputStreamReader; 324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.Reader; 334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.ArrayList; 341de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawaimport java.util.Collection; 354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashSet; 364560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawaimport java.util.List; 374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Set; 384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/** 404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 41677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * Basic implementation achieving vCard parsing. Based on vCard 2.1. 424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @hide 444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/* package */ class VCardParserImpl_V21 { 4602117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa private static final String LOG_TAG = VCardConstants.LOG_TAG; 474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 48147f1ae5371954ae845cb2330b221df6ca1d8831Daisuke Miyakawa protected static final class CustomBufferedReader extends BufferedReader { 494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private long mTime; 504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 51f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa /** 52f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa * Needed since "next line" may be null due to end of line. 53f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa */ 54f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa private boolean mNextLineIsValid; 55f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa private String mNextLine; 56f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public CustomBufferedReader(Reader in) { 584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa super(in); 594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa @Override 624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public String readLine() throws IOException { 63f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (mNextLineIsValid) { 64f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String ret = mNextLine; 65f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mNextLine = null; 66f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mNextLineIsValid = false; 67f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa return ret; 68f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 69f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 70677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa final long start = System.currentTimeMillis(); 71f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String line = super.readLine(); 72677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa final long end = System.currentTimeMillis(); 734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mTime += end - start; 74f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa return line; 75f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 76f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 77f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa /** 78f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa * Read one line, but make this object store it in its queue. 79f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa */ 80f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa public String peekLine() throws IOException { 81f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (!mNextLineIsValid) { 82677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa final long start = System.currentTimeMillis(); 83f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String line = super.readLine(); 84677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa final long end = System.currentTimeMillis(); 85f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mTime += end - start; 86f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 87f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mNextLine = line; 88f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mNextLineIsValid = true; 89f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 90f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 91f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa return mNextLine; 924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public long getTotalmillisecond() { 954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return mTime; 964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final String DEFAULT_ENCODING = "8BIT"; 1001de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa private static final String DEFAULT_CHARSET = "UTF-8"; 1014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected final String mIntermediateCharset; 1034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1041de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa private final List<VCardInterpreter> mInterpreterList = new ArrayList<VCardInterpreter>(); 1051de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa private boolean mCanceled; 10648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 1074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 1084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 1094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * The encoding type for deconding byte streams. This member variable is 1104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * reset to a default encoding every time when a new item comes. 1114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 1124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 1134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "Encoding" in vCard is different from "Charset". It is mainly used for 1144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * addresses, notes, images. "7BIT", "8BIT", "BASE64", and 1154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "QUOTED-PRINTABLE" are known examples. 1164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 1174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 1184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String mCurrentEncoding; 1194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1201de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected String mCurrentCharset; 1211de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 1224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 1234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 1244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * The reader object to be used internally. 1254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Developers should not directly read a line from this object. Use 1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * getLine() unless there some reason. 1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 131f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa protected CustomBufferedReader mReader; 1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 1344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Set for storing unkonwn TYPE attributes, which is not acceptable in vCard 1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * specification, but happens to be seen in real world vCard. 1374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 138677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * <p> 139677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * We just accept those invalid types after emitting a warning for each of it. 140677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * </p> 1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 1424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected final Set<String> mUnknownTypeSet = new HashSet<String>(); 1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 1454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Set for storing unkonwn VALUE attributes, which is not acceptable in 1474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vCard specification, but happens to be seen in real world vCard. 1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 149677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * <p> 150677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * We just accept those invalid types after emitting a warning for each of it. 151677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * </p> 1524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 1534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected final Set<String> mUnknownValueSet = new HashSet<String>(); 1544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public VCardParserImpl_V21() { 1574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa this(VCardConfig.VCARD_TYPE_DEFAULT); 1584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public VCardParserImpl_V21(int vcardType) { 1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mIntermediateCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; 1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return true when a given property name is a valid property name. 1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected boolean isValidPropertyName(final String propertyName) { 1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(getKnownPropertyNameSet().contains(propertyName.toUpperCase()) || 1694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa propertyName.startsWith("X-")) 1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa && !mUnknownTypeSet.contains(propertyName)) { 1714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mUnknownTypeSet.add(propertyName); 1724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName); 1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 1754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return String. It may be null, or its length may be 0 1794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws IOException 1804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 1814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getLine() throws IOException { 1824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return mReader.readLine(); 1834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 185f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa protected String peekLine() throws IOException { 186f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa return mReader.peekLine(); 187f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 188f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 1894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return String with it's length > 0 1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws IOException 1924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws VCardException when the stream reached end of line 1934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 1944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getNonEmptyLine() throws IOException, VCardException { 1954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String line; 1964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 1974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa line = getLine(); 1984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line == null) { 1994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Reached end of buffer."); 2004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (line.trim().length() > 0) { 2014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return line; 2024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 20648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa /** 20748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * <code> 2084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF 2094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * items *CRLF 2104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "END" [ws] ":" [ws] "VCARD" 21148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * </code> 21256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa * @return False when reaching end of file. 2134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 21448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private boolean parseOneVCard() throws IOException, VCardException { 21548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // reset for this entire vCard. 21648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mCurrentEncoding = DEFAULT_ENCODING; 2171de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa mCurrentCharset = DEFAULT_CHARSET; 2184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 21948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa boolean allowGarbage = false; 2204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!readBeginVCard(allowGarbage)) { 2214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 2224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2231de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 2241de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onEntryStarted(); 2251de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 2264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa parseItems(); 2271de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 2281de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onEntryEnded(); 2291de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 2304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 2314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 2344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return True when successful. False when reaching the end of line 2354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws IOException 2364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws VCardException 2374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 2384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException { 23948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // TODO: use consructPropertyLine(). 2404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String line; 2414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa do { 2424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 2434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa line = getLine(); 2444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line == null) { 2454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 2464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (line.trim().length() > 0) { 2474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 2484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 250f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String[] strArray = line.split(":", 2); 251f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final int length = strArray.length; 2524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 253f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // Although vCard 2.1/3.0 specification does not allow lower cases, 254f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // we found vCard file emitted by some external vCard expoter have such 2554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // invalid Strings. 25648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // e.g. BEGIN:vCard 2574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (length == 2 && strArray[0].trim().equalsIgnoreCase("BEGIN") 2584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa && strArray[1].trim().equalsIgnoreCase("VCARD")) { 2594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 2604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (!allowGarbage) { 26148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throw new VCardException("Expected String \"BEGIN:VCARD\" did not come " 26248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa + "(Instead, \"" + line + "\" came)"); 2634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } while (allowGarbage); 2654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Reached where must not be reached."); 2674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 27048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * Parses lines other than the first "BEGIN:VCARD". Takes care of "END:VCARD"n and 27148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * "BEGIN:VCARD" in nested vCard. 2724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 2744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * items = *CRLF item / item 27548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * 27648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * Note: BEGIN/END aren't include in the original spec while this method handles them. 2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected void parseItems() throws IOException, VCardException { 2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa boolean ended = false; 2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 28148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa try { 28248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa ended = parseItem(); 28348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } catch (VCardInvalidCommentLineException e) { 28448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored."); 2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (!ended) { 2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa try { 2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa ended = parseItem(); 2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } catch (VCardInvalidCommentLineException e) { 2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored."); 2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * item = [groups "."] name [params] ":" value CRLF / [groups "."] "ADR" 2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * [params] ":" addressparts CRLF / [groups "."] "ORG" [params] ":" orgparts 2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * CRLF / [groups "."] "N" [params] ":" nameparts CRLF / [groups "."] 3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "AGENT" [params] ":" vcard CRLF 3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected boolean parseItem() throws IOException, VCardException { 30348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // Reset for an item. 3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mCurrentEncoding = DEFAULT_ENCODING; 3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String line = getNonEmptyLine(); 3071de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final VCardProperty propertyData = constructPropertyData(line); 3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 30948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa final String propertyNameUpper = propertyData.getName().toUpperCase(); 3101de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final String propertyRawValue = propertyData.getRawValue(); 31148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 31248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (propertyNameUpper.equals(VCardConstants.PROPERTY_BEGIN)) { 3131de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa if (propertyRawValue.equalsIgnoreCase("VCARD")) { 31448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleNest(); 31548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else { 3161de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa throw new VCardException("Unknown BEGIN type: " + propertyRawValue); 31748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 31848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else if (propertyNameUpper.equals(VCardConstants.PROPERTY_END)) { 3191de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa if (propertyRawValue.equalsIgnoreCase("VCARD")) { 32048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return true; // Ended. 32148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else { 3221de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa throw new VCardException("Unknown END type: " + propertyRawValue); 32348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 32448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else { 3251de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa parseItemInter(propertyData, propertyNameUpper); 3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 32748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return false; 32848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 32948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 3301de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa private void parseItemInter(VCardProperty property, String propertyNameUpper) 33148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throws IOException, VCardException { 3321de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa String propertyRawValue = property.getRawValue(); 3331de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa if (propertyNameUpper.equals(VCardConstants.PROPERTY_AGENT)) { 3341de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa handleAgent(property); 33548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else if (isValidPropertyName(propertyNameUpper)) { 33648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (propertyNameUpper.equals(VCardConstants.PROPERTY_VERSION) && 3371de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa !propertyRawValue.equals(getVersionString())) { 3381de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa throw new VCardVersionException( 3391de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa "Incompatible version: " + propertyRawValue + " != " + getVersionString()); 3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3411de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa handlePropertyValue(property, propertyNameUpper); 34248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else { 34348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throw new VCardException("Unknown property name: \"" + propertyNameUpper + "\""); 3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 34548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 34748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private void handleNest() throws IOException, VCardException { 3481de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 3491de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onEntryStarted(); 3501de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 35148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa parseItems(); 3521de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 3531de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onEntryEnded(); 3541de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // For performance reason, the states for group and property name are merged into one. 3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa static private final int STATE_GROUP_OR_PROPERTY_NAME = 0; 3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa static private final int STATE_PARAMS = 1; 3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not. 3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa static private final int STATE_PARAMS_IN_DQUOTE = 2; 3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3631de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected VCardProperty constructPropertyData(String line) throws VCardException { 3641de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final VCardProperty propertyData = new VCardProperty(); 36548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int length = line.length(); 3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (length > 0 && line.charAt(0) == '#') { 3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardInvalidCommentLineException(); 3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int state = STATE_GROUP_OR_PROPERTY_NAME; 3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int nameIndex = 0; 3734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // This loop is developed so that we don't have to take care of bottle neck here. 3754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Refactor carefully when you need to do so. 3764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i++) { 3774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final char ch = line.charAt(i); 3784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa switch (state) { 3794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa case STATE_GROUP_OR_PROPERTY_NAME: { 3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == ':') { // End of a property name. 3814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String propertyName = line.substring(nameIndex, i); 38248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.setName(propertyName); 38348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.setRawValue( i < length - 1 ? line.substring(i + 1) : ""); 38448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return propertyData; 3854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (ch == '.') { // Each group is followed by the dot. 3864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String groupName = line.substring(nameIndex, i); 3874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (groupName.length() == 0) { 3884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, "Empty group found. Ignoring."); 3894560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } else { 39048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.addGroup(groupName); 3914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa nameIndex = i + 1; // Next should be another group or a property name. 393677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa } else if (ch == ';') { // End of property name and beginneng of parameters. 3944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String propertyName = line.substring(nameIndex, i); 39548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.setName(propertyName); 3964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa nameIndex = i + 1; 3974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa state = STATE_PARAMS; // Start parameter parsing. 3984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 399d5a8fc2a35c69fc34df35fd545ccf83d548ba50cDaisuke Miyakawa // TODO: comma support (in vCard 3.0 and 4.0). 4004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 4014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa case STATE_PARAMS: { 4034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == '"') { 4044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) { 4054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " + 4064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa "Silently allow it"); 4074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa state = STATE_PARAMS_IN_DQUOTE; 4094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (ch == ';') { // Starts another param. 41048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleParams(propertyData, line.substring(nameIndex, i)); 4114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa nameIndex = i + 1; 4124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (ch == ':') { // End of param and beginenning of values. 41348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleParams(propertyData, line.substring(nameIndex, i)); 41448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.setRawValue(i < length - 1 ? line.substring(i + 1) : ""); 41548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return propertyData; 4164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 4184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa case STATE_PARAMS_IN_DQUOTE: { 4204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == '"') { 4214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) { 4224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " + 4234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa "Silently allow it"); 4244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa state = STATE_PARAMS; 4264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 4284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardInvalidLineException("Invalid line: \"" + line + "\""); 4334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 4364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * params = ";" [ws] paramlist paramlist = paramlist [ws] ";" [ws] param / 4374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * param param = "TYPE" [ws] "=" [ws] ptypeval / "VALUE" [ws] "=" [ws] 4384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * pvalueval / "ENCODING" [ws] "=" [ws] pencodingval / "CHARSET" [ws] "=" 4394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * [ws] charsetval / "LANGUAGE" [ws] "=" [ws] langval / "X-" word [ws] "=" 4404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * [ws] word / knowntype 4414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 4421de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected void handleParams(VCardProperty propertyData, String params) 44348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throws VCardException { 4444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String[] strArray = params.split("=", 2); 4454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (strArray.length == 2) { 4464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String paramName = strArray[0].trim().toUpperCase(); 4474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String paramValue = strArray[1].trim(); 4484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (paramName.equals("TYPE")) { 44948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleType(propertyData, paramValue); 4504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (paramName.equals("VALUE")) { 45148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleValue(propertyData, paramValue); 4524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (paramName.equals("ENCODING")) { 45348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleEncoding(propertyData, paramValue); 4544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (paramName.equals("CHARSET")) { 45548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleCharset(propertyData, paramValue); 4564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (paramName.equals("LANGUAGE")) { 45748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleLanguage(propertyData, paramValue); 4584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (paramName.startsWith("X-")) { 45948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleAnyParam(propertyData, paramName, paramValue); 4604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 4614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Unknown type \"" + paramName + "\""); 4624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 46448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleParamWithoutName(propertyData, strArray[0]); 4654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 4694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vCard 3.0 parser implementation may throw VCardException. 4704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 4711de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected void handleParamWithoutName(VCardProperty propertyData, final String paramValue) { 47248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleType(propertyData, paramValue); 4734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 4764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * ptypeval = knowntype / "X-" word 4774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 4781de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected void handleType(VCardProperty propertyData, final String ptypeval) { 4794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(getKnownTypeSet().contains(ptypeval.toUpperCase()) 4804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa || ptypeval.startsWith("X-")) 4814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa && !mUnknownTypeSet.contains(ptypeval)) { 4824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mUnknownTypeSet.add(ptypeval); 4834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval)); 4844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4851de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa propertyData.addParameter(VCardConstants.PARAM_TYPE, ptypeval); 4864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 4894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word 4904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 4911de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected void handleValue(VCardProperty propertyData, final String pvalueval) { 4924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(getKnownValueSet().contains(pvalueval.toUpperCase()) 4934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa || pvalueval.startsWith("X-") 4944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa || mUnknownValueSet.contains(pvalueval))) { 4954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mUnknownValueSet.add(pvalueval); 4964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, String.format( 4974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa "The value unsupported by TYPE of %s: ", getVersion(), pvalueval)); 4984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4991de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa propertyData.addParameter(VCardConstants.PARAM_VALUE, pvalueval); 5004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 5034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word 5044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 5051de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected void handleEncoding(VCardProperty propertyData, String pencodingval) 50648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throws VCardException { 5074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (getAvailableEncodingSet().contains(pencodingval) || 5084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa pencodingval.startsWith("X-")) { 5091de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa propertyData.addParameter(VCardConstants.PARAM_ENCODING, pencodingval); 51048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // Update encoding right away, as this is needed to understanding other params. 5114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mCurrentEncoding = pencodingval; 5124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 5134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Unknown encoding \"" + pencodingval + "\""); 5144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 5184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 5194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521), 5204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc. 5214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * We allow any charset. 5224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 5234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 5241de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected void handleCharset(VCardProperty propertyData, String charsetval) { 5251de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa mCurrentCharset = charsetval; 5261de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa propertyData.addParameter(VCardConstants.PARAM_CHARSET, charsetval); 5274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 5304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * See also Section 7.1 of RFC 1521 5314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 5321de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected void handleLanguage(VCardProperty propertyData, String langval) 53348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throws VCardException { 5344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String[] strArray = langval.split("-"); 5354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (strArray.length != 2) { 5364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Invalid Language: \"" + langval + "\""); 5374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String tmp = strArray[0]; 5394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int length = tmp.length(); 5404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i++) { 5414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!isAsciiLetter(tmp.charAt(i))) { 5424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Invalid Language: \"" + langval + "\""); 5434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa tmp = strArray[1]; 5464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa length = tmp.length(); 5474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i++) { 5484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!isAsciiLetter(tmp.charAt(i))) { 5494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Invalid Language: \"" + langval + "\""); 5504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5521de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa propertyData.addParameter(VCardConstants.PARAM_LANGUAGE, langval); 5534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private boolean isAsciiLetter(char ch) { 5564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { 5574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 5584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 5604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 5634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Mainly for "X-" type. This accepts any kind of type without check. 5644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 56548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected void handleAnyParam( 5661de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa VCardProperty propertyData, String paramName, String paramValue) { 5671de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa propertyData.addParameter(paramName, paramValue); 5684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5701de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected void handlePropertyValue(VCardProperty property, String propertyName) 5714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throws IOException, VCardException { 5721de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final String propertyNameUpper = property.getName().toUpperCase(); 5731de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa String propertyRawValue = property.getRawValue(); 5741de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final String sourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; 5751de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final Collection<String> charsetCollection = 5761de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa property.getParameters(VCardConstants.PARAM_CHARSET); 5771de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa String targetCharset = 5781de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa ((charsetCollection != null) ? charsetCollection.iterator().next() : null); 5791de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa if (TextUtils.isEmpty(targetCharset)) { 5801de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa targetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET; 5811de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 5821de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 5831de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa // TODO: have "separableProperty" which reflects vCard spec.. 5841de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa if (propertyNameUpper.equals(VCardConstants.PROPERTY_ADR) 5851de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa || propertyNameUpper.equals(VCardConstants.PROPERTY_ORG) 5861de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa || propertyNameUpper.equals(VCardConstants.PROPERTY_N)) { 5879e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng handleAdrOrgN(property, propertyRawValue, sourceCharset, targetCharset); 5881de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa return; 5891de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 5901de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 5914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String upperEncoding = mCurrentEncoding.toUpperCase(); 5924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) { 5931de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final String quotedPrintablePart = getQuotedPrintablePart(propertyRawValue); 5941de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final String propertyEncodedValue = 5951de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa VCardUtils.parseQuotedPrintable(quotedPrintablePart, 5961de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa false, sourceCharset, targetCharset); 5971de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa property.setRawValue(quotedPrintablePart); 5981de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa property.setValues(propertyEncodedValue); 5991de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 6001de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 6011de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64) 6034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa || upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) { 6044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // It is very rare, but some BASE64 data may be so big that 6054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // OutOfMemoryError occurs. To ignore such cases, use try-catch. 6064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa try { 6072c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng final String base64Property = getBase64(propertyRawValue); 6082c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng try { 6092c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng property.setByteValue(Base64.decode(base64Property, Base64.DEFAULT)); 6102c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng } catch (IllegalArgumentException e) { 6112c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng throw new VCardException("Decode error on base64 photo: " + propertyRawValue); 6122c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng } 6131de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 6141de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 6151de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } catch (OutOfMemoryError error) { 6174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!"); 6181de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 6191de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 6201de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 6234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") || 6244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa upperEncoding.startsWith("X-"))) { 6254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, 6264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String.format("The encoding \"%s\" is unsupported by vCard %s", 6274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mCurrentEncoding, getVersionString())); 6284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 630f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // Some device uses line folding defined in RFC 2425, which is not allowed 631f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // in vCard 2.1 (while needed in vCard 3.0). 632f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 633f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // e.g. 634f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // BEGIN:VCARD 635f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // VERSION:2.1 636f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // N:;Omega;;; 637f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // EMAIL;INTERNET:"Omega" 638f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // <omega@example.com> 639f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // FN:Omega 640f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // END:VCARD 641f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 642f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // The vCard above assumes that email address should become: 643f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // "Omega" <omega@example.com> 644f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 645f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // But vCard 2.1 requires Quote-Printable when a line contains line break(s). 646f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 647f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // For more information about line folding, 648f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // see "5.8.1. Line delimiting and folding" in RFC 2425. 649f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 650f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // We take care of this case more formally in vCard 3.0, so we only need to 651f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // do this in vCard 2.1. 652be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (getVersion() == VCardConfig.VERSION_21) { 653f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa StringBuilder builder = null; 654f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa while (true) { 655f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String nextLine = peekLine(); 656f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // We don't need to care too much about this exceptional case, 657f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // but we should not wrongly eat up "END:VCARD", since it critically 658f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // breaks this parser's state machine. 659f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // Thus we roughly look over the next line and confirm it is at least not 660f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // "END:VCARD". This extra fee is worth paying. This is exceptional 661f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // anyway. 662f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (!TextUtils.isEmpty(nextLine) && 663f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa nextLine.charAt(0) == ' ' && 664f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa !"END:VCARD".contains(nextLine.toUpperCase())) { 665f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa getLine(); // Drop the next line. 666f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 667f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (builder == null) { 668f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa builder = new StringBuilder(); 6691de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa builder.append(propertyRawValue); 670f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 671f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa builder.append(nextLine.substring(1)); 672f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } else { 673f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa break; 674f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 675f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 676f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (builder != null) { 6771de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa propertyRawValue = builder.toString(); 678f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 679f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 680f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 6811de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa ArrayList<String> propertyValueList = new ArrayList<String>(); 6821de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa String value = VCardUtils.convertStringCharset( 6831de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa maybeUnescapeText(propertyRawValue), sourceCharset, targetCharset); 6841de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa propertyValueList.add(value); 6851de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa property.setValues(propertyValueList); 6861de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 6871de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 6881de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6929e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng private void handleAdrOrgN(VCardProperty property, String propertyRawValue, 6939e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng String sourceCharset, String targetCharset) throws VCardException, IOException { 6949e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng List<String> encodedValueList = new ArrayList<String>(); 6959e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 6969e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some softwares/devices emit 6979e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // such data. 6989e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng if (mCurrentEncoding.equalsIgnoreCase(VCardConstants.PARAM_ENCODING_QP)) { 6999e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // First we retrieve Quoted-Printable String from vCard entry, which may include 7009e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // multiple lines. 7019e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng final String quotedPrintablePart = getQuotedPrintablePart(propertyRawValue); 7029e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 7039e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // "Raw value" from the view of users should contain all part of QP string. 7049e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // TODO: add test for this handling 7059e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng property.setRawValue(quotedPrintablePart); 7069e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 7079e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // We split Quoted-Printable String using semi-colon before decoding it, as 7089e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // the Quoted-Printable may have semi-colon, which confuses splitter. 7099e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng final List<String> quotedPrintableValueList = 7109e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng VCardUtils.constructListFromValue(quotedPrintablePart, getVersion()); 7119e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng for (String quotedPrintableValue : quotedPrintableValueList) { 7129e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng String encoded = VCardUtils.parseQuotedPrintable(quotedPrintableValue, 7139e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng false, sourceCharset, targetCharset); 7149e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng encodedValueList.add(encoded); 7159e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 7169e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } else { 7179e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng final String propertyValue = getPotentialMultiline(propertyRawValue); 7189e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng final List<String> rawValueList = 7199e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng VCardUtils.constructListFromValue(propertyValue, getVersion()); 7209e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng for (String rawValue : rawValueList) { 7219e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng encodedValueList.add(VCardUtils.convertStringCharset( 7229e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng rawValue, sourceCharset, targetCharset)); 7239e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 7249e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 7259e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 7269e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng property.setValues(encodedValueList); 7279e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng for (VCardInterpreter interpreter : mInterpreterList) { 7289e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng interpreter.onPropertyCreated(property); 7299e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 7309e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 7319e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 7324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 7334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 7344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Parses and returns Quoted-Printable. 7354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 7364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 7374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @param firstString The string following a parameter name and attributes. 7384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Example: "string" in 7394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "ADR:ENCODING=QUOTED-PRINTABLE:string\n\r". 7404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return whole Quoted-Printable string, including a given argument and 7414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * following lines. Excludes the last empty line following to Quoted 7424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Printable lines. 7434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws IOException 7444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws VCardException 7454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 7461de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa private String getQuotedPrintablePart(String firstString) 7471de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa throws IOException, VCardException { 7484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Specifically, there may be some padding between = and CRLF. 7494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // See the following: 7504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // 7514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // qp-line := *(qp-segment transport-padding CRLF) 7524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // qp-part transport-padding 7534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // qp-segment := qp-section *(SPACE / TAB) "=" 7544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // ; Maximum length of 76 characters 7554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // 7564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // e.g. (from RFC 2045) 7574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Now's the time = 7584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // for all folk to come= 7594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // to the aid of their country. 7604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (firstString.trim().endsWith("=")) { 7614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // remove "transport-padding" 7624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int pos = firstString.length() - 1; 7634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (firstString.charAt(pos) != '=') { 7644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa StringBuilder builder = new StringBuilder(); 7664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(firstString.substring(0, pos + 1)); 7674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append("\r\n"); 7684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String line; 7694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 7704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa line = getLine(); 7714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line == null) { 7724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("File ended during parsing a Quoted-Printable String"); 7734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line.trim().endsWith("=")) { 7754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // remove "transport-padding" 7764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa pos = line.length() - 1; 7774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (line.charAt(pos) != '=') { 7784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(line.substring(0, pos + 1)); 7804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append("\r\n"); 7814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 7824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(line); 7834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 7844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return builder.toString(); 7874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 7884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return firstString; 7894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7929e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng /** 7939e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * Given the first line of a property, checks consecutive lines after it and builds a new 7949e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * multi-line value if it exists. 7959e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * 7969e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * @param firstString The first line of the property. 7979e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * @return A new property, potentially built from multiple lines. 7989e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * @throws IOException 7999e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng */ 8009e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng private String getPotentialMultiline(String firstString) throws IOException { 8019e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng final StringBuilder builder = new StringBuilder(); 8029e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng builder.append(firstString); 8039e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 8049e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng while (true) { 8059e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng final String line = peekLine(); 8069e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng if (line == null || line.length() == 0) { 8079e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng break; 8089e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 8099e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 8109e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng final String propertyName = getPropertyNameUpperCase(line); 8119e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng if (propertyName != null) { 8129e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng break; 8139e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 8149e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 8159e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // vCard 2.1 does not allow multi-line of adr but microsoft vcards may have it. 8169e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // We will consider the next line to be a part of a multi-line value if it does not 8179e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // contain a property name (i.e. a colon or semi-colon). 8189e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // Consume the line. 8199e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng getLine(); 8209e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng builder.append(" ").append(line); 8219e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 8229e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 8239e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng return builder.toString(); 8249e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 8259e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 8264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getBase64(String firstString) throws IOException, VCardException { 827c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa final StringBuilder builder = new StringBuilder(); 8284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(firstString); 8294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 831c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa final String line = peekLine(); 8324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line == null) { 8334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("File ended during parsing BASE64 binary"); 8344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 835c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa 836c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // vCard 2.1 requires two spaces at the end of BASE64 strings, but some vCard doesn't 837a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng // have them. We try to detect those cases using colon and semi-colon, given BASE64 838a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng // does not contain it. 839a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng // E.g. 840a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng // TEL;TYPE=WORK:+5555555 841a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng // or 842a4e576ce8ebf869c505f0fe0ea82faefae9940f8Chiao Cheng // END:VCARD 8439e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng String propertyName = getPropertyNameUpperCase(line); 8449e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng if (getKnownPropertyNameSet().contains(propertyName)) { 8459e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng Log.w(LOG_TAG, "Found a next property during parsing a BASE64 string, " + 8469e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng "which must not contain semi-colon or colon. Treat the line as next " 8479e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng + "property."); 8489e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng Log.w(LOG_TAG, "Problematic line: " + line.trim()); 8499e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng break; 850c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa } 851c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa 852c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // Consume the line. 853c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa getLine(); 854c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa 8554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line.length() == 0) { 8564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 8574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(line); 8594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return builder.toString(); 8624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8649e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng /** 8659e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * Extracts the property name portion of a given vCard line. 8669e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * <p> 8679e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * Properties must contain a colon. 8689e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * <p> 8699e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * E.g. 8709e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * TEL;TYPE=WORK:+5555555 // returns "TEL" 8719e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * END:VCARD // returns "END" 8729e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * TEL; // returns null 8739e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * 8749e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * @param line The vCard line. 8759e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng * @return The property name portion. {@literal null} if no property name found. 8769e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng */ 8779e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng private String getPropertyNameUpperCase(String line) { 8789e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng final int colonIndex = line.indexOf(":"); 8799e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng if (colonIndex > -1) { 8809e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng final int semiColonIndex = line.indexOf(";"); 8819e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 8829e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng // Find the minimum index that is greater than -1. 8839e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng final int minIndex; 8849e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng if (colonIndex == -1) { 8859e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng minIndex = semiColonIndex; 8869e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } else if (semiColonIndex == -1) { 8879e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng minIndex = colonIndex; 8889e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } else { 8899e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng minIndex = Math.min(colonIndex, semiColonIndex); 8909e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 8919e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng return line.substring(0, minIndex).toUpperCase(); 8929e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 8939e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng return null; 8949e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng } 8959e87c7606b0593b6789cdd177d80e31b991c864cChiao Cheng 8964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 8974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vCard 2.1 specifies AGENT allows one vcard entry. Currently we emit an 8984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * error toward the AGENT property. 8994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * // TODO: Support AGENT property. 9004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * item = 9014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * ... / [groups "."] "AGENT" [params] ":" vcard CRLF vcard = "BEGIN" [ws] 9024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] ":" [ws] "VCARD" 9034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9041de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected void handleAgent(final VCardProperty property) throws VCardException { 9051de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa if (!property.getRawValue().toUpperCase().contains("BEGIN:VCARD")) { 9064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Apparently invalid line seen in Windows Mobile 6.5. Ignore them. 9071de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 9081de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 9091de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 9104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return; 9114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 9124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardAgentNotSupportedException("AGENT Property is not supported now."); 9134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 9174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * For vCard 3.0. 9184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String maybeUnescapeText(final String text) { 9204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return text; 9214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 9244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Returns unescaped String if the character should be unescaped. Return 9254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * null otherwise. e.g. In vCard 2.1, "\;" should be unescaped into ";" 9264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * while "\x" should not be. 9274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9284560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa protected String maybeUnescapeCharacter(final char ch) { 9294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return unescapeCharacter(ch); 9304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* package */ static String unescapeCharacter(final char ch) { 9334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Original vCard 2.1 specification does not allow transformation 9344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous 9354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // implementation of 9364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // this class allowed them, so keep it as is. 9374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') { 9384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return String.valueOf(ch); 9394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 9404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return null; 9414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 945be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa * @return {@link VCardConfig#VERSION_21} 9464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected int getVersion() { 948be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa return VCardConfig.VERSION_21; 9494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 952be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa * @return {@link VCardConfig#VERSION_30} 9534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getVersionString() { 9554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardConstants.VERSION_V21; 9564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getKnownPropertyNameSet() { 9594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sKnownPropertyNameSet; 9604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getKnownTypeSet() { 9634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sKnownTypeSet; 9644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getKnownValueSet() { 9674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sKnownValueSet; 9684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getAvailableEncodingSet() { 9714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sAvailableEncoding; 9724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getDefaultEncoding() { 9754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return DEFAULT_ENCODING; 9764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9781de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected String getDefaultCharset() { 9791de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa return DEFAULT_CHARSET; 9801de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 9814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9821de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected String getCurrentCharset() { 9831de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa return mCurrentCharset; 9841de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 9851de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 9861de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa public void addInterpreter(VCardInterpreter interpreter) { 9871de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa mInterpreterList.add(interpreter); 9881de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 9891de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 9901de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa public void parse(InputStream is) throws IOException, VCardException { 9914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (is == null) { 9924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new NullPointerException("InputStream must not be null."); 9934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset); 996f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mReader = new CustomBufferedReader(tmpReader); 9974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final long start = System.currentTimeMillis(); 9991de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 10001de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onVCardStarted(); 10014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 100256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa 100356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa // vcard_file = [wsls] vcard [wsls] 100456650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa while (true) { 100556650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa synchronized (this) { 100656650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa if (mCanceled) { 100756650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa Log.i(LOG_TAG, "Cancel request has come. exitting parse operation."); 100856650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa break; 100956650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 101056650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 101156650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa if (!parseOneVCard()) { 101256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa break; 101356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 101456650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 101556650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa 101656650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 101756650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa interpreter.onVCardEnded(); 101856650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 101956650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 102056650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa 102156650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa public void parseOne(InputStream is) throws IOException, VCardException { 102256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa if (is == null) { 102356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa throw new NullPointerException("InputStream must not be null."); 102456650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 102556650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa 102656650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset); 102756650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa mReader = new CustomBufferedReader(tmpReader); 102856650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa 102956650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa final long start = System.currentTimeMillis(); 103056650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 103156650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa interpreter.onVCardStarted(); 103256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 103356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa parseOneVCard(); 10341de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 10351de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onVCardEnded(); 10364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 10374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 10384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 10391de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa public final synchronized void cancel() { 1040b23043e3a199e951817b6d86c301b9521d4c26feDaisuke Miyakawa Log.i(LOG_TAG, "ParserImpl received cancel operation."); 10414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mCanceled = true; 10424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 10434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa} 1044