VCardParserImpl_V21.java revision 48dd8e86a81d2ab40eb762975c8211c225002bf0
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; 2548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawaimport android.util.Log; 2648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.BufferedReader; 284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.IOException; 294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.InputStream; 304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.InputStreamReader; 314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.Reader; 324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.ArrayList; 334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashSet; 344560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawaimport java.util.List; 354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Set; 364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/** 384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 39677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * Basic implementation achieving vCard parsing. Based on vCard 2.1. 404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @hide 424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/* package */ class VCardParserImpl_V21 { 4402117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa private static final String LOG_TAG = VCardConstants.LOG_TAG; 454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 464560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa private static final class EmptyInterpreter implements VCardInterpreter { 474560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 484560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void end() { 494560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 514560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void endEntry() { 524560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 534560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 544560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void endProperty() { 554560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 564560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 574560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void propertyGroup(String group) { 584560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 594560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 604560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void propertyName(String name) { 614560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 624560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 634560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void propertyParamType(String type) { 644560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 654560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 664560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void propertyParamValue(String value) { 674560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 684560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 694560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void propertyValues(List<String> values) { 704560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 714560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 724560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void start() { 734560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 744560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 754560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void startEntry() { 764560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 774560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa @Override 784560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa public void startProperty() { 794560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 804560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 814560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa 82147f1ae5371954ae845cb2330b221df6ca1d8831Daisuke Miyakawa protected static final class CustomBufferedReader extends BufferedReader { 834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private long mTime; 844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 85f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa /** 86f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa * Needed since "next line" may be null due to end of line. 87f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa */ 88f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa private boolean mNextLineIsValid; 89f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa private String mNextLine; 90f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public CustomBufferedReader(Reader in) { 924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa super(in); 934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa @Override 964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public String readLine() throws IOException { 97f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (mNextLineIsValid) { 98f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String ret = mNextLine; 99f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mNextLine = null; 100f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mNextLineIsValid = false; 101f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa return ret; 102f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 103f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 104677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa final long start = System.currentTimeMillis(); 105f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String line = super.readLine(); 106677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa final long end = System.currentTimeMillis(); 1074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mTime += end - start; 108f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa return line; 109f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 110f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 111f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa /** 112f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa * Read one line, but make this object store it in its queue. 113f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa */ 114f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa public String peekLine() throws IOException { 115f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (!mNextLineIsValid) { 116677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa final long start = System.currentTimeMillis(); 117f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String line = super.readLine(); 118677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa final long end = System.currentTimeMillis(); 119f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mTime += end - start; 120f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 121f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mNextLine = line; 122f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mNextLineIsValid = true; 123f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 124f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 125f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa return mNextLine; 1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public long getTotalmillisecond() { 1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return mTime; 1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 13348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa /** 13448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * Stores param information for a property. Used with {@link PropertyData}. 13548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * 13648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * e.g. "LANGUAGE=jp" -> paramName = "LANGUAGE", paramvalue = "jp" 13748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa */ 13848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private class ParamPair { 13948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public String paramName; 14048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public String paramValue; 14148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public ParamPair(String paramName, String paramValue) { 14248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa this.paramName = paramName; 14348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa this.paramValue = paramValue; 14448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 14548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa @Override 14648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public String toString() { 14748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return paramName + ", " + paramValue; 14848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 14948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 15048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 15148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa /** 15248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * Intermediate data for storing property. 15348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * 15448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * Name, group and param are property encoded when this object is prepared. 15548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * Value isn't encoded yet at this point. 15648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa */ 15748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected class PropertyData { 15848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private String mName; 15948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private List<String> mGroupList; 16048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private List<ParamPair> mParamList; 16148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private String mRawValue; 16248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 16348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public void setName(String name) throws VCardException { 16448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (mName != null) { 16548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throw new VCardException( 16648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa String.format("Property name is re-defined " + 16748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa "(existing: %s, requested: %s", mName, name)); 16848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 16948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mName = name; 17048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 17148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 17248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public void addGroup(String group) { 17348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (mGroupList == null) { 17448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mGroupList = new ArrayList<String>(); 17548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 17648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mGroupList.add(group); 17748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 17848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 17948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public void addParam(String paramName, String paramValue) { 18048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (mParamList == null) { 18148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mParamList = new ArrayList<ParamPair>(); 18248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 18348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mParamList.add(new ParamPair(paramName, paramValue)); 18448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 18548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 18648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public void setRawValue(String rawValue) throws VCardException { 18748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (mRawValue != null) { 18848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throw new VCardException( 18948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa String.format("Property value is re-defined " + 19048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa "(existing: %s, requested: %s", mRawValue, rawValue)); 19148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 19248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mRawValue = rawValue; 19348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 19448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 19548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public String getName() { 19648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return mName; 19748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 19848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 19948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public List<String> getGroupList() { 20048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return mGroupList; 20148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 20248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 20348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public List<ParamPair> getParamList() { 20448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return mParamList; 20548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 20648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 20748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa public String getRawValue() { 20848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return mRawValue; 20948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 21048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 21148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 2124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final String DEFAULT_ENCODING = "8BIT"; 2134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 21448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // TODO: remove this. 2154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected boolean mCanceled; 2164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected final String mIntermediateCharset; 2174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 21848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private VCardInterpreter mInterpreter; 21948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 2204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 2214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 2224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * The encoding type for deconding byte streams. This member variable is 2234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * reset to a default encoding every time when a new item comes. 2244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 2254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 2264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "Encoding" in vCard is different from "Charset". It is mainly used for 2274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * addresses, notes, images. "7BIT", "8BIT", "BASE64", and 2284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "QUOTED-PRINTABLE" are known examples. 2294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 2304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 2314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String mCurrentEncoding; 2324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 2344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 2354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * The reader object to be used internally. 2364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 2374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 2384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Developers should not directly read a line from this object. Use 2394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * getLine() unless there some reason. 2404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 2414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 242f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa protected CustomBufferedReader mReader; 2434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 2454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 2464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Set for storing unkonwn TYPE attributes, which is not acceptable in vCard 2474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * specification, but happens to be seen in real world vCard. 2484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 249677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * <p> 250677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * We just accept those invalid types after emitting a warning for each of it. 251677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * </p> 2524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 2534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected final Set<String> mUnknownTypeSet = new HashSet<String>(); 2544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 2564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 2574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Set for storing unkonwn VALUE attributes, which is not acceptable in 2584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vCard specification, but happens to be seen in real world vCard. 2594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 260677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * <p> 261677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * We just accept those invalid types after emitting a warning for each of it. 262677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa * </p> 2634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 2644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected final Set<String> mUnknownValueSet = new HashSet<String>(); 2654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public VCardParserImpl_V21() { 2684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa this(VCardConfig.VCARD_TYPE_DEFAULT); 2694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public VCardParserImpl_V21(int vcardType) { 2724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mIntermediateCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; 2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Parses the file at the given position. 2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // <pre class="prettyprint">vcard_file = [wsls] vcard [wsls]</pre> 2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected void parseVCardFile() throws IOException, VCardException { 2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (mCanceled) { 284b23043e3a199e951817b6d86c301b9521d4c26feDaisuke Miyakawa Log.i(LOG_TAG, "Cancel request has come. exitting parse operation."); 2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 28748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (!parseOneVCard()) { 2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return true when a given property name is a valid property name. 2954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected boolean isValidPropertyName(final String propertyName) { 2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(getKnownPropertyNameSet().contains(propertyName.toUpperCase()) || 2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa propertyName.startsWith("X-")) 2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa && !mUnknownTypeSet.contains(propertyName)) { 3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mUnknownTypeSet.add(propertyName); 3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName); 3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return String. It may be null, or its length may be 0 3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws IOException 3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getLine() throws IOException { 3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return mReader.readLine(); 3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 314f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa protected String peekLine() throws IOException { 315f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa return mReader.peekLine(); 316f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 317f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return String with it's length > 0 3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws IOException 3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws VCardException when the stream reached end of line 3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getNonEmptyLine() throws IOException, VCardException { 3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String line; 3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa line = getLine(); 3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line == null) { 3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Reached end of buffer."); 3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (line.trim().length() > 0) { 3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return line; 3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 33548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa /** 33648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * <code> 3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF 3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * items *CRLF 3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "END" [ws] ":" [ws] "VCARD" 34048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * </code> 3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 34248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private boolean parseOneVCard() throws IOException, VCardException { 34348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // reset for this entire vCard. 34448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mCurrentEncoding = DEFAULT_ENCODING; 3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 34648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa boolean allowGarbage = false; 3474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!readBeginVCard(allowGarbage)) { 3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 3494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa mInterpreter.startEntry(); 3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa parseItems(); 3524560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa mInterpreter.endEntry(); 3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 3544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return True when successful. False when reaching the end of line 3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws IOException 3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws VCardException 3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException { 36248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // TODO: use consructPropertyLine(). 3634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String line; 3644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa do { 3654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa line = getLine(); 3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line == null) { 3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (line.trim().length() > 0) { 3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 373f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String[] strArray = line.split(":", 2); 374f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final int length = strArray.length; 3754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 376f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // Although vCard 2.1/3.0 specification does not allow lower cases, 377f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // we found vCard file emitted by some external vCard expoter have such 3784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // invalid Strings. 37948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // e.g. BEGIN:vCard 3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (length == 2 && strArray[0].trim().equalsIgnoreCase("BEGIN") 3814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa && strArray[1].trim().equalsIgnoreCase("VCARD")) { 3824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 3834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (!allowGarbage) { 38448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throw new VCardException("Expected String \"BEGIN:VCARD\" did not come " 38548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa + "(Instead, \"" + line + "\" came)"); 3864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } while (allowGarbage); 3884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Reached where must not be reached."); 3904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 39348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * Parses lines other than the first "BEGIN:VCARD". Takes care of "END:VCARD"n and 39448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * "BEGIN:VCARD" in nested vCard. 3954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 3964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 3974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * items = *CRLF item / item 39848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * 39948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa * Note: BEGIN/END aren't include in the original spec while this method handles them. 4004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 4014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected void parseItems() throws IOException, VCardException { 4024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa boolean ended = false; 4034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 40448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa try { 40548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa ended = parseItem(); 40648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } catch (VCardInvalidCommentLineException e) { 40748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored."); 4084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (!ended) { 4114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa try { 4124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa ended = parseItem(); 4134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } catch (VCardInvalidCommentLineException e) { 4144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored."); 4154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 4204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * item = [groups "."] name [params] ":" value CRLF / [groups "."] "ADR" 4214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * [params] ":" addressparts CRLF / [groups "."] "ORG" [params] ":" orgparts 4224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * CRLF / [groups "."] "N" [params] ":" nameparts CRLF / [groups "."] 4234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "AGENT" [params] ":" vcard CRLF 4244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 4254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected boolean parseItem() throws IOException, VCardException { 42648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // Reset for an item. 4274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mCurrentEncoding = DEFAULT_ENCODING; 4284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String line = getNonEmptyLine(); 43048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa final PropertyData propertyData = constructPropertyData(line); 4314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 43248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa final String propertyNameUpper = propertyData.getName().toUpperCase(); 43348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa final String propertyValue = propertyData.getRawValue(); 43448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 43548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (propertyNameUpper.equals(VCardConstants.PROPERTY_BEGIN)) { 43648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (propertyValue.equalsIgnoreCase("VCARD")) { 43748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleNest(); 43848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else { 43948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throw new VCardException("Unknown BEGIN type: " + propertyValue); 44048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 44148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else if (propertyNameUpper.equals(VCardConstants.PROPERTY_END)) { 44248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (propertyValue.equalsIgnoreCase("VCARD")) { 44348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return true; // Ended. 44448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else { 44548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throw new VCardException("Unknown END type: " + propertyValue); 44648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 44748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else { 44848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mInterpreter.startProperty(); 44948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa sendPropertyLineMetaInfo(propertyData); 45048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa parseItemInter(propertyNameUpper, propertyValue); 45148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mInterpreter.endProperty(); 4524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 45348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return false; 45448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 45548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 45648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private void sendPropertyLineMetaInfo(PropertyData propertyData) { 45748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa final List<String> groupList = propertyData.getGroupList(); 45848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (groupList != null) { 45948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa for (String group : groupList) { 46048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mInterpreter.propertyGroup(group); 46148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 4624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 46348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mInterpreter.propertyName(propertyData.getName()); 4644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 46548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa final List<ParamPair> paramList = propertyData.getParamList(); 46648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (paramList != null) { 46748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa for (ParamPair pair : paramList) { 46848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mInterpreter.propertyParamType(pair.paramName); 46948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mInterpreter.propertyParamValue(pair.paramValue); 47048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 47148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 47248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 4734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 47448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private void parseItemInter(String propertyNameUpper, String propertyValue) 47548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throws IOException, VCardException { 47648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (propertyNameUpper.equals(VCardConstants.PROPERTY_ADR) 47748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa || propertyNameUpper.equals(VCardConstants.PROPERTY_ORG) 47848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa || propertyNameUpper.equals(VCardConstants.PROPERTY_N)) { 47948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleMultiplePropertyValue(propertyNameUpper, propertyValue); 48048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else if (propertyNameUpper.equals(VCardConstants.PROPERTY_AGENT)) { 4814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa handleAgent(propertyValue); 48248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else if (isValidPropertyName(propertyNameUpper)) { 48348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa if (propertyNameUpper.equals(VCardConstants.PROPERTY_VERSION) && 48402117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa !propertyValue.equals(getVersionString())) { 4854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardVersionException("Incompatible version: " + propertyValue + " != " 4864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa + getVersionString()); 4874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 48848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handlePropertyValue(propertyNameUpper, propertyValue); 48948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } else { 49048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throw new VCardException("Unknown property name: \"" + propertyNameUpper + "\""); 4914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 49248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa } 4934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 49448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa private void handleNest() throws IOException, VCardException { 49548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mInterpreter.startEntry(); 49648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa parseItems(); 49748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa mInterpreter.endEntry(); 4984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // For performance reason, the states for group and property name are merged into one. 5014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa static private final int STATE_GROUP_OR_PROPERTY_NAME = 0; 5024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa static private final int STATE_PARAMS = 1; 5034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not. 5044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa static private final int STATE_PARAMS_IN_DQUOTE = 2; 5054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 50648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected PropertyData constructPropertyData(String line) throws VCardException { 50748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa final PropertyData propertyData = new PropertyData(); 50848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa 5094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int length = line.length(); 5104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (length > 0 && line.charAt(0) == '#') { 5114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardInvalidCommentLineException(); 5124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int state = STATE_GROUP_OR_PROPERTY_NAME; 5154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int nameIndex = 0; 5164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // This loop is developed so that we don't have to take care of bottle neck here. 5184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Refactor carefully when you need to do so. 5194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i++) { 5204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final char ch = line.charAt(i); 5214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa switch (state) { 5224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa case STATE_GROUP_OR_PROPERTY_NAME: { 5234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == ':') { // End of a property name. 5244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String propertyName = line.substring(nameIndex, i); 52548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.setName(propertyName); 52648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.setRawValue( i < length - 1 ? line.substring(i + 1) : ""); 52748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return propertyData; 5284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (ch == '.') { // Each group is followed by the dot. 5294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String groupName = line.substring(nameIndex, i); 5304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (groupName.length() == 0) { 5314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, "Empty group found. Ignoring."); 5324560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } else { 53348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.addGroup(groupName); 5344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa nameIndex = i + 1; // Next should be another group or a property name. 536677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa } else if (ch == ';') { // End of property name and beginneng of parameters. 5374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String propertyName = line.substring(nameIndex, i); 53848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.setName(propertyName); 5394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa nameIndex = i + 1; 5404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa state = STATE_PARAMS; // Start parameter parsing. 5414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 542d5a8fc2a35c69fc34df35fd545ccf83d548ba50cDaisuke Miyakawa // TODO: comma support (in vCard 3.0 and 4.0). 5434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 5444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa case STATE_PARAMS: { 5464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == '"') { 5474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) { 5484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " + 5494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa "Silently allow it"); 5504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa state = STATE_PARAMS_IN_DQUOTE; 5524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (ch == ';') { // Starts another param. 55348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleParams(propertyData, line.substring(nameIndex, i)); 5544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa nameIndex = i + 1; 5554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (ch == ':') { // End of param and beginenning of values. 55648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleParams(propertyData, line.substring(nameIndex, i)); 55748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.setRawValue(i < length - 1 ? line.substring(i + 1) : ""); 55848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa return propertyData; 5594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 5614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa case STATE_PARAMS_IN_DQUOTE: { 5634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == '"') { 5644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) { 5654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " + 5664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa "Silently allow it"); 5674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa state = STATE_PARAMS; 5694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 5714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardInvalidLineException("Invalid line: \"" + line + "\""); 5764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 5794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * params = ";" [ws] paramlist paramlist = paramlist [ws] ";" [ws] param / 5804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * param param = "TYPE" [ws] "=" [ws] ptypeval / "VALUE" [ws] "=" [ws] 5814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * pvalueval / "ENCODING" [ws] "=" [ws] pencodingval / "CHARSET" [ws] "=" 5824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * [ws] charsetval / "LANGUAGE" [ws] "=" [ws] langval / "X-" word [ws] "=" 5834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * [ws] word / knowntype 5844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 58548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected void handleParams(PropertyData propertyData, String params) 58648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throws VCardException { 5874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String[] strArray = params.split("=", 2); 5884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (strArray.length == 2) { 5894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String paramName = strArray[0].trim().toUpperCase(); 5904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String paramValue = strArray[1].trim(); 5914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (paramName.equals("TYPE")) { 59248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleType(propertyData, paramValue); 5934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (paramName.equals("VALUE")) { 59448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleValue(propertyData, paramValue); 5954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (paramName.equals("ENCODING")) { 59648dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleEncoding(propertyData, paramValue); 5974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (paramName.equals("CHARSET")) { 59848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleCharset(propertyData, paramValue); 5994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (paramName.equals("LANGUAGE")) { 60048dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleLanguage(propertyData, paramValue); 6014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (paramName.startsWith("X-")) { 60248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleAnyParam(propertyData, paramName, paramValue); 6034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 6044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Unknown type \"" + paramName + "\""); 6054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 60748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleParamWithoutName(propertyData, strArray[0]); 6084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 6124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vCard 3.0 parser implementation may throw VCardException. 6134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 61448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected void handleParamWithoutName(PropertyData propertyData, final String paramValue) { 61548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa handleType(propertyData, paramValue); 6164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 6194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * ptypeval = knowntype / "X-" word 6204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 62148dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected void handleType(PropertyData propertyData, final String ptypeval) { 6224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(getKnownTypeSet().contains(ptypeval.toUpperCase()) 6234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa || ptypeval.startsWith("X-")) 6244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa && !mUnknownTypeSet.contains(ptypeval)) { 6254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mUnknownTypeSet.add(ptypeval); 6264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval)); 6274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 62848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.addParam(VCardConstants.PARAM_TYPE, ptypeval); 6294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 6324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word 6334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 63448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected void handleValue(PropertyData propertyData, final String pvalueval) { 6354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(getKnownValueSet().contains(pvalueval.toUpperCase()) 6364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa || pvalueval.startsWith("X-") 6374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa || mUnknownValueSet.contains(pvalueval))) { 6384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mUnknownValueSet.add(pvalueval); 6394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, String.format( 6404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa "The value unsupported by TYPE of %s: ", getVersion(), pvalueval)); 6414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 64248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.addParam(VCardConstants.PARAM_VALUE, pvalueval); 6434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 6464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word 6474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 64848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected void handleEncoding(PropertyData propertyData, String pencodingval) 64948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throws VCardException { 6504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (getAvailableEncodingSet().contains(pencodingval) || 6514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa pencodingval.startsWith("X-")) { 65248dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.addParam(VCardConstants.PARAM_ENCODING, pencodingval); 65348dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa // Update encoding right away, as this is needed to understanding other params. 6544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mCurrentEncoding = pencodingval; 6554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 6564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Unknown encoding \"" + pencodingval + "\""); 6574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 6614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 6624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521), 6634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc. 6644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * We allow any charset. 6654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 6664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 66748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected void handleCharset(PropertyData propertyData, String charsetval) { 66848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.addParam(VCardConstants.PARAM_CHARSET, charsetval); 6694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 6724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * See also Section 7.1 of RFC 1521 6734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 67448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected void handleLanguage(PropertyData propertyData, String langval) 67548dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa throws VCardException { 6764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String[] strArray = langval.split("-"); 6774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (strArray.length != 2) { 6784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Invalid Language: \"" + langval + "\""); 6794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String tmp = strArray[0]; 6814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int length = tmp.length(); 6824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i++) { 6834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!isAsciiLetter(tmp.charAt(i))) { 6844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Invalid Language: \"" + langval + "\""); 6854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa tmp = strArray[1]; 6884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa length = tmp.length(); 6894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i++) { 6904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!isAsciiLetter(tmp.charAt(i))) { 6914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("Invalid Language: \"" + langval + "\""); 6924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 69448dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.addParam(VCardConstants.PARAM_LANGUAGE, langval); 6954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private boolean isAsciiLetter(char ch) { 6984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { 6994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 7004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 7024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 7054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Mainly for "X-" type. This accepts any kind of type without check. 7064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 70748dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa protected void handleAnyParam( 70848dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa PropertyData propertyData, String paramName, String paramValue) { 70948dd8e86a81d2ab40eb762975c8211c225002bf0Daisuke Miyakawa propertyData.addParam(paramName, paramValue); 7104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected void handlePropertyValue(String propertyName, String propertyValue) 7134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throws IOException, VCardException { 7144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String upperEncoding = mCurrentEncoding.toUpperCase(); 7154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) { 7164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String result = getQuotedPrintable(propertyValue); 7174560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa final ArrayList<String> v = new ArrayList<String>(); 7184560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa v.add(result); 7194560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa mInterpreter.propertyValues(v); 7204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64) 7214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa || upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) { 7224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // It is very rare, but some BASE64 data may be so big that 7234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // OutOfMemoryError occurs. To ignore such cases, use try-catch. 7244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa try { 7254560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa final ArrayList<String> arrayList = new ArrayList<String>(); 7264560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa arrayList.add(getBase64(propertyValue)); 7274560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa mInterpreter.propertyValues(arrayList); 7284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } catch (OutOfMemoryError error) { 7294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!"); 7304560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa mInterpreter.propertyValues(null); 7314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 7334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") || 7344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa upperEncoding.startsWith("X-"))) { 7354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, 7364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String.format("The encoding \"%s\" is unsupported by vCard %s", 7374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mCurrentEncoding, getVersionString())); 7384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 740f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // Some device uses line folding defined in RFC 2425, which is not allowed 741f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // in vCard 2.1 (while needed in vCard 3.0). 742f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 743f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // e.g. 744f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // BEGIN:VCARD 745f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // VERSION:2.1 746f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // N:;Omega;;; 747f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // EMAIL;INTERNET:"Omega" 748f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // <omega@example.com> 749f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // FN:Omega 750f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // END:VCARD 751f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 752f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // The vCard above assumes that email address should become: 753f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // "Omega" <omega@example.com> 754f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 755f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // But vCard 2.1 requires Quote-Printable when a line contains line break(s). 756f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 757f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // For more information about line folding, 758f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // see "5.8.1. Line delimiting and folding" in RFC 2425. 759f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // 760f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // We take care of this case more formally in vCard 3.0, so we only need to 761f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // do this in vCard 2.1. 762be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (getVersion() == VCardConfig.VERSION_21) { 763f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa StringBuilder builder = null; 764f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa while (true) { 765f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa final String nextLine = peekLine(); 766f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // We don't need to care too much about this exceptional case, 767f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // but we should not wrongly eat up "END:VCARD", since it critically 768f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // breaks this parser's state machine. 769f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // Thus we roughly look over the next line and confirm it is at least not 770f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // "END:VCARD". This extra fee is worth paying. This is exceptional 771f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa // anyway. 772f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (!TextUtils.isEmpty(nextLine) && 773f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa nextLine.charAt(0) == ' ' && 774f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa !"END:VCARD".contains(nextLine.toUpperCase())) { 775f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa getLine(); // Drop the next line. 776f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 777f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (builder == null) { 778f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa builder = new StringBuilder(); 779f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa builder.append(propertyValue); 780f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 781f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa builder.append(nextLine.substring(1)); 782f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } else { 783f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa break; 784f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 785f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 786f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa if (builder != null) { 787f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa propertyValue = builder.toString(); 788f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 789f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa } 790f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa 7914560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa ArrayList<String> v = new ArrayList<String>(); 7924560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa v.add(maybeUnescapeText(propertyValue)); 7934560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa mInterpreter.propertyValues(v); 7944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 7984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 7994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Parses and returns Quoted-Printable. 8004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 8014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 8024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @param firstString The string following a parameter name and attributes. 8034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Example: "string" in 8044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * "ADR:ENCODING=QUOTED-PRINTABLE:string\n\r". 8054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return whole Quoted-Printable string, including a given argument and 8064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * following lines. Excludes the last empty line following to Quoted 8074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Printable lines. 8084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws IOException 8094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @throws VCardException 8104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 8114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private String getQuotedPrintable(String firstString) throws IOException, VCardException { 8124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Specifically, there may be some padding between = and CRLF. 8134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // See the following: 8144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // 8154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // qp-line := *(qp-segment transport-padding CRLF) 8164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // qp-part transport-padding 8174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // qp-segment := qp-section *(SPACE / TAB) "=" 8184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // ; Maximum length of 76 characters 8194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // 8204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // e.g. (from RFC 2045) 8214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Now's the time = 8224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // for all folk to come= 8234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // to the aid of their country. 8244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (firstString.trim().endsWith("=")) { 8254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // remove "transport-padding" 8264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int pos = firstString.length() - 1; 8274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (firstString.charAt(pos) != '=') { 8284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa StringBuilder builder = new StringBuilder(); 8304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(firstString.substring(0, pos + 1)); 8314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append("\r\n"); 8324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String line; 8334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 8344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa line = getLine(); 8354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line == null) { 8364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("File ended during parsing a Quoted-Printable String"); 8374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line.trim().endsWith("=")) { 8394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // remove "transport-padding" 8404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa pos = line.length() - 1; 8414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (line.charAt(pos) != '=') { 8424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(line.substring(0, pos + 1)); 8444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append("\r\n"); 8454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 8464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(line); 8474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 8484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return builder.toString(); 8514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 8524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return firstString; 8534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getBase64(String firstString) throws IOException, VCardException { 857c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa final StringBuilder builder = new StringBuilder(); 8584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(firstString); 8594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (true) { 861c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa final String line = peekLine(); 8624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line == null) { 8634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardException("File ended during parsing BASE64 binary"); 8644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 865c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa 866c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // vCard 2.1 requires two spaces at the end of BASE64 strings, but some vCard doesn't 867c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // have them. We try to detect those cases using semi-colon, given BASE64 doesn't 868c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // contain it. Specifically BASE64 doesn't have semi-colon in it, so we should be able 869c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // to detect the case safely. 870c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa if (line.contains(":")) { 871c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa if (getKnownPropertyNameSet().contains( 872c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa line.substring(0, line.indexOf(":")).toUpperCase())) { 873c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa Log.w(LOG_TAG, "Found a next property during parsing a BASE64 string, " + 874c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa "which must not contain semi-colon. Treat the line as next property."); 875c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa Log.w(LOG_TAG, "Problematic line: " + line.trim()); 876c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa break; 877c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa } 878c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa } 879c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa 880c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa // Consume the line. 881c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa getLine(); 882c955c8b0da0c9fcbad0ddcae76641358c27e72cdDaisuke Miyakawa 8834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line.length() == 0) { 8844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 8854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(line); 8874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return builder.toString(); 8904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 8934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 8944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Mainly for "ADR", "ORG", and "N" 8954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 8964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 8974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 8984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * addressparts = 0*6(strnosemi ";") strnosemi ; PO Box, Extended Addr, 8994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Street, Locality, Region, Postal Code, Country Name orgparts = 9004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * *(strnosemi ";") strnosemi ; First is Organization Name, remainder are 9014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Organization Units. nameparts = 0*4(strnosemi ";") strnosemi ; Family, 9024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Given, Middle, Prefix, Suffix. ; Example:Public;John;Q.;Reverend Dr.;III, 9034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Esq. strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi ; To include a 9044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * semicolon in this string, it must be escaped ; with a "\" character. We 9054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * do not care the number of "strnosemi" here. We are not sure whether we 9064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * should add "\" CRLF to each value. We exclude them for now. 9074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected void handleMultiplePropertyValue(String propertyName, String propertyValue) 9094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throws IOException, VCardException { 9104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some 9114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // softwares/devices 9124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // emit such data. 9134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (mCurrentEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { 9144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa propertyValue = getQuotedPrintable(propertyValue); 9154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9174560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa mInterpreter.propertyValues(VCardUtils.constructListFromValue(propertyValue, 9184560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa getVersion())); 9194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* 9224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vCard 2.1 specifies AGENT allows one vcard entry. Currently we emit an 9234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * error toward the AGENT property. 9244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * // TODO: Support AGENT property. 9254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * item = 9264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * ... / [groups "."] "AGENT" [params] ":" vcard CRLF vcard = "BEGIN" [ws] 9274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] ":" [ws] "VCARD" 9284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected void handleAgent(final String propertyValue) throws VCardException { 9304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!propertyValue.toUpperCase().contains("BEGIN:VCARD")) { 9314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Apparently invalid line seen in Windows Mobile 6.5. Ignore them. 9324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return; 9334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 9344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new VCardAgentNotSupportedException("AGENT Property is not supported now."); 9354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 9394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * For vCard 3.0. 9404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String maybeUnescapeText(final String text) { 9424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return text; 9434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 9464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Returns unescaped String if the character should be unescaped. Return 9474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * null otherwise. e.g. In vCard 2.1, "\;" should be unescaped into ";" 9484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * while "\x" should not be. 9494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9504560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa protected String maybeUnescapeCharacter(final char ch) { 9514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return unescapeCharacter(ch); 9524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /* package */ static String unescapeCharacter(final char ch) { 9554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Original vCard 2.1 specification does not allow transformation 9564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous 9574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // implementation of 9584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // this class allowed them, so keep it as is. 9594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') { 9604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return String.valueOf(ch); 9614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 9624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return null; 9634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 967be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa * @return {@link VCardConfig#VERSION_21} 9684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected int getVersion() { 970be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa return VCardConfig.VERSION_21; 9714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 974be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa * @return {@link VCardConfig#VERSION_30} 9754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 9764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getVersionString() { 9774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardConstants.VERSION_V21; 9784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getKnownPropertyNameSet() { 9814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sKnownPropertyNameSet; 9824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getKnownTypeSet() { 9854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sKnownTypeSet; 9864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getKnownValueSet() { 9894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sKnownValueSet; 9904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected Set<String> getAvailableEncodingSet() { 9934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return VCardParser_V21.sAvailableEncoding; 9944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 9964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa protected String getDefaultEncoding() { 9974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return DEFAULT_ENCODING; 9984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 9994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 10004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 10014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public void parse(InputStream is, VCardInterpreter interpreter) 10024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throws IOException, VCardException { 10034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (is == null) { 10044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa throw new NullPointerException("InputStream must not be null."); 10054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 10064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 10074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset); 1008f6d9e0eeae38a72481ce2e19d0872d3f8f81189fDaisuke Miyakawa mReader = new CustomBufferedReader(tmpReader); 10094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 10104560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa mInterpreter = (interpreter != null ? interpreter : new EmptyInterpreter()); 10114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 10124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final long start = System.currentTimeMillis(); 10134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (mInterpreter != null) { 10144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mInterpreter.start(); 10154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 10164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa parseVCardFile(); 10174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (mInterpreter != null) { 10184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mInterpreter.end(); 10194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 10204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 10214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 10224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public final void cancel() { 1023b23043e3a199e951817b6d86c301b9521d4c26feDaisuke Miyakawa Log.i(LOG_TAG, "ParserImpl received cancel operation."); 10244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mCanceled = true; 10254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 10264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa} 1027