VCardParserImpl_V21.java revision 56650608f09fc75f260c03e00456ef3d1e60c929
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 184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardAgentNotSupportedException; 194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardException; 204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardInvalidCommentLineException; 214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardInvalidLineException; 224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport com.android.vcard.exception.VCardVersionException; 234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawaimport android.text.TextUtils; 251de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawaimport android.util.Base64; 2648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawaimport android.util.Log; 2748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke 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)) { 5871de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa List<String> encodedValueList = new ArrayList<String>(); 5881de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 5891de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some softwares/devices emit 5901de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa // such data. 5911de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa if (mCurrentEncoding.equalsIgnoreCase(VCardConstants.PARAM_ENCODING_QP)) { 5921de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa // First we retrieve Quoted-Printable String from vCard entry, which may include 5931de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa // multiple lines. 5941de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final String quotedPrintablePart = getQuotedPrintablePart(propertyRawValue); 5951de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 5961de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa // "Raw value" from the view of users should contain all part of QP string. 5971de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa // TODO: add test for this handling 5981de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa property.setRawValue(quotedPrintablePart); 5991de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 6001de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa // We split Quoted-Printable String using semi-colon before decoding it, as 6011de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa // the Quoted-Printable may have semi-colon, which confuses splitter. 6021de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final List<String> quotedPrintableValueList = 6031de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa VCardUtils.constructListFromValue(quotedPrintablePart, getVersion()); 6041de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (String quotedPrintableValue : quotedPrintableValueList) { 6051de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa String encoded = VCardUtils.parseQuotedPrintable(quotedPrintableValue, 6061de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa false, sourceCharset, targetCharset); 6071de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa encodedValueList.add(encoded); 6081de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6091de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } else { 6101de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final List<String> rawValueList = 6111de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa VCardUtils.constructListFromValue(propertyRawValue, getVersion()); 6121de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (String rawValue : rawValueList) { 6131de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa encodedValueList.add(VCardUtils.convertStringCharset( 6141de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa rawValue, sourceCharset, targetCharset)); 6151de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6161de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6171de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 6181de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa property.setValues(encodedValueList); 6191de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 6201de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 6211de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6221de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa return; 6231de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6241de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 6254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String upperEncoding = mCurrentEncoding.toUpperCase(); 6264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) { 6271de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final String quotedPrintablePart = getQuotedPrintablePart(propertyRawValue); 6281de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final String propertyEncodedValue = 6291de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa VCardUtils.parseQuotedPrintable(quotedPrintablePart, 6301de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa false, sourceCharset, targetCharset); 6311de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa property.setRawValue(quotedPrintablePart); 6321de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa property.setValues(propertyEncodedValue); 6331de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 6341de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 6351de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64) 6374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa || upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) { 6384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // It is very rare, but some BASE64 data may be so big that 6394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // OutOfMemoryError occurs. To ignore such cases, use try-catch. 6404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa try { 6411de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa property.setByteValue(Base64.decode(getBase64(propertyRawValue), Base64.DEFAULT)); 6421de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 6431de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 6441de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } catch (OutOfMemoryError error) { 6464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!"); 6471de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 6481de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 6491de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 6504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 6524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") || 6534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa upperEncoding.startsWith("X-"))) { 6544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, 6554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String.format("The encoding \"%s\" is unsupported by vCard %s", 6564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mCurrentEncoding, getVersionString())); 6574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 659f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // Some device uses line folding defined in RFC 2425, which is not allowed 660f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // in vCard 2.1 (while needed in vCard 3.0). 661f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 662f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // e.g. 663f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // BEGIN:VCARD 664f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // VERSION:2.1 665f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // N:;Omega;;; 666f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // EMAIL;INTERNET:"Omega" 667f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // <omega@example.com> 668f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // FN:Omega 669f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // END:VCARD 670f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 671f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // The vCard above assumes that email address should become: 672f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // "Omega" <omega@example.com> 673f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 674f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // But vCard 2.1 requires Quote-Printable when a line contains line break(s). 675f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 676f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // For more information about line folding, 677f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // see "5.8.1. Line delimiting and folding" in RFC 2425. 678f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 679f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // We take care of this case more formally in vCard 3.0, so we only need to 680f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // do this in vCard 2.1. 681be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (getVersion() == VCardConfig.VERSION_21) { 682f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa StringBuilder builder = null; 683f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa while (true) { 684f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String nextLine = peekLine(); 685f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // We don't need to care too much about this exceptional case, 686f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // but we should not wrongly eat up "END:VCARD", since it critically 687f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // breaks this parser's state machine. 688f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // Thus we roughly look over the next line and confirm it is at least not 689f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // "END:VCARD". This extra fee is worth paying. This is exceptional 690f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // anyway. 691f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (!TextUtils.isEmpty(nextLine) && 692f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa nextLine.charAt(0) == ' ' && 693f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa !"END:VCARD".contains(nextLine.toUpperCase())) { 694f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa getLine(); // Drop the next line. 695f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 696f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (builder == null) { 697f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa builder = new StringBuilder(); 6981de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa builder.append(propertyRawValue); 699f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 700f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa builder.append(nextLine.substring(1)); 701f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } else { 702f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa break; 703f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 704f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 705f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (builder != null) { 7061de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa propertyRawValue = builder.toString(); 707f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 708f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 709f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 7101de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa ArrayList<String> propertyValueList = new ArrayList<String>(); 7111de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa String value = VCardUtils.convertStringCharset( 7121de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa maybeUnescapeText(propertyRawValue), sourceCharset, targetCharset); 7131de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa propertyValueList.add(value); 7141de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa property.setValues(propertyValueList); 7151de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 7161de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 7171de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 7184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 7224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 7234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Parses and returns Quoted-Printable. 7244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 7254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 7264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @param firstString The string following a parameter name and attributes. 7274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Example: "string" in 7284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "ADR:ENCODING=QUOTED-PRINTABLE:string\n\r". 7294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return whole Quoted-Printable string, including a given argument and 7304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * following lines. Excludes the last empty line following to Quoted 7314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Printable lines. 7324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws IOException 7334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws VCardException 7344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 7351de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa private String getQuotedPrintablePart(String firstString) 7361de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa throws IOException, VCardException { 7374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Specifically, there may be some padding between = and CRLF. 7384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // See the following: 7394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // 7404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // qp-line := *(qp-segment transport-padding CRLF) 7414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // qp-part transport-padding 7424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // qp-segment := qp-section *(SPACE / TAB) "=" 7434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // ; Maximum length of 76 characters 7444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // 7454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // e.g. (from RFC 2045) 7464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Now's the time = 7474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // for all folk to come= 7484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // to the aid of their country. 7494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (firstString.trim().endsWith("=")) { 7504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // remove "transport-padding" 7514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int pos = firstString.length() - 1; 7524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (firstString.charAt(pos) != '=') { 7534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa StringBuilder builder = new StringBuilder(); 7554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(firstString.substring(0, pos + 1)); 7564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append("\r\n"); 7574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String line; 7584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 7594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa line = getLine(); 7604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line == null) { 7614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("File ended during parsing a Quoted-Printable String"); 7624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line.trim().endsWith("=")) { 7644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // remove "transport-padding" 7654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa pos = line.length() - 1; 7664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (line.charAt(pos) != '=') { 7674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(line.substring(0, pos + 1)); 7694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append("\r\n"); 7704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 7714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(line); 7724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 7734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return builder.toString(); 7764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 7774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return firstString; 7784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getBase64(String firstString) throws IOException, VCardException { 782c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa final StringBuilder builder = new StringBuilder(); 7834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(firstString); 7844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 786c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa final String line = peekLine(); 7874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line == null) { 7884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("File ended during parsing BASE64 binary"); 7894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 790c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa 791c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // vCard 2.1 requires two spaces at the end of BASE64 strings, but some vCard doesn't 792c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // have them. We try to detect those cases using semi-colon, given BASE64 doesn't 793c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // contain it. Specifically BASE64 doesn't have semi-colon in it, so we should be able 794c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // to detect the case safely. 795c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa if (line.contains(":")) { 796c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa if (getKnownPropertyNameSet().contains( 797c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa line.substring(0, line.indexOf(":")).toUpperCase())) { 798c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa Log.w(LOG_TAG, "Found a next property during parsing a BASE64 string, " + 799c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa "which must not contain semi-colon. Treat the line as next property."); 800c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa Log.w(LOG_TAG, "Problematic line: " + line.trim()); 801c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa break; 802c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa } 803c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa } 804c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa 805c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // Consume the line. 806c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa getLine(); 807c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa 8084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line.length() == 0) { 8094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 8104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(line); 8124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return builder.toString(); 8154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 8184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vCard 2.1 specifies AGENT allows one vcard entry. Currently we emit an 8194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * error toward the AGENT property. 8204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * // TODO: Support AGENT property. 8214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * item = 8224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * ... / [groups "."] "AGENT" [params] ":" vcard CRLF vcard = "BEGIN" [ws] 8234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] ":" [ws] "VCARD" 8244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 8251de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected void handleAgent(final VCardProperty property) throws VCardException { 8261de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa if (!property.getRawValue().toUpperCase().contains("BEGIN:VCARD")) { 8274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Apparently invalid line seen in Windows Mobile 6.5. Ignore them. 8281de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 8291de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onPropertyCreated(property); 8301de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 8314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return; 8324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 8334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardAgentNotSupportedException("AGENT Property is not supported now."); 8344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 8384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * For vCard 3.0. 8394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 8404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String maybeUnescapeText(final String text) { 8414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return text; 8424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 8454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Returns unescaped String if the character should be unescaped. Return 8464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * null otherwise. e.g. In vCard 2.1, "\;" should be unescaped into ";" 8474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * while "\x" should not be. 8484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 8494560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa protected String maybeUnescapeCharacter(final char ch) { 8504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return unescapeCharacter(ch); 8514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* package */ static String unescapeCharacter(final char ch) { 8544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Original vCard 2.1 specification does not allow transformation 8554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous 8564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // implementation of 8574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // this class allowed them, so keep it as is. 8584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') { 8594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return String.valueOf(ch); 8604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 8614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return null; 8624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 866be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa * @return {@link VCardConfig#VERSION_21} 8674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 8684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected int getVersion() { 869be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa return VCardConfig.VERSION_21; 8704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 873be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa * @return {@link VCardConfig#VERSION_30} 8744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 8754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getVersionString() { 8764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardConstants.VERSION_V21; 8774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getKnownPropertyNameSet() { 8804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sKnownPropertyNameSet; 8814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getKnownTypeSet() { 8844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sKnownTypeSet; 8854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getKnownValueSet() { 8884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sKnownValueSet; 8894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getAvailableEncodingSet() { 8924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sAvailableEncoding; 8934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getDefaultEncoding() { 8964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return DEFAULT_ENCODING; 8974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8991de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected String getDefaultCharset() { 9001de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa return DEFAULT_CHARSET; 9011de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 9024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9031de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa protected String getCurrentCharset() { 9041de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa return mCurrentCharset; 9051de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 9061de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 9071de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa public void addInterpreter(VCardInterpreter interpreter) { 9081de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa mInterpreterList.add(interpreter); 9091de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa } 9101de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa 9111de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa public void parse(InputStream is) throws IOException, VCardException { 9124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (is == null) { 9134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new NullPointerException("InputStream must not be null."); 9144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset); 917f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mReader = new CustomBufferedReader(tmpReader); 9184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final long start = System.currentTimeMillis(); 9201de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 9211de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onVCardStarted(); 9224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 92356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa 92456650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa // vcard_file = [wsls] vcard [wsls] 92556650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa while (true) { 92656650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa synchronized (this) { 92756650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa if (mCanceled) { 92856650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa Log.i(LOG_TAG, "Cancel request has come. exitting parse operation."); 92956650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa break; 93056650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 93156650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 93256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa if (!parseOneVCard()) { 93356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa break; 93456650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 93556650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 93656650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa 93756650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 93856650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa interpreter.onVCardEnded(); 93956650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 94056650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 94156650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa 94256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa public void parseOne(InputStream is) throws IOException, VCardException { 94356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa if (is == null) { 94456650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa throw new NullPointerException("InputStream must not be null."); 94556650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 94656650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa 94756650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset); 94856650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa mReader = new CustomBufferedReader(tmpReader); 94956650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa 95056650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa final long start = System.currentTimeMillis(); 95156650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 95256650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa interpreter.onVCardStarted(); 95356650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa } 95456650608f09fc75f260c03e00456ef3d1e60c929Daisuke Miyakawa parseOneVCard(); 9551de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa for (VCardInterpreter interpreter : mInterpreterList) { 9561de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa interpreter.onVCardEnded(); 9574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9601de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa public final synchronized void cancel() { 961b23043e3a199e951817b6d86c301b9521d4c26feDaisuke Miyakawa Log.i(LOG_TAG, "ParserImpl received cancel operation."); 9624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mCanceled = true; 9634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa} 965