VCardEntryConstructor.java revision 02117b3d19787ff65486b9f9db8abd338ae4c9f9
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.android.vcard; 17 18import android.accounts.Account; 19import android.text.TextUtils; 20import android.util.Base64; 21import android.util.Log; 22 23import java.util.ArrayList; 24import java.util.Collection; 25import java.util.List; 26 27/** 28 * <p> 29 * The {@link VCardInterpreter} implementation which enables {@link VCardEntryHandler} objects 30 * to easily handle each vCard entry. 31 * </p> 32 * <p> 33 * This class understand details inside vCard and translates it to {@link VCardEntry}. 34 * Then the class throw it to {@link VCardEntryHandler} registered via 35 * {@link #addEntryHandler(VCardEntryHandler)}, so that all those registered objects 36 * are able to handle the {@link VCardEntry} object. 37 * </p> 38 * <p> 39 * If you want to know the detail inside vCard, it would be better to implement 40 * {@link VCardInterpreter} directly, instead of relying on this class and 41 * {@link VCardEntry} created by the object. 42 * </p> 43 */ 44public class VCardEntryConstructor implements VCardInterpreter { 45 private static String LOG_TAG = VCardConstants.LOG_TAG; 46 47 private VCardEntry.Property mCurrentProperty = new VCardEntry.Property(); 48 private VCardEntry mCurrentVCardEntry; 49 private String mParamType; 50 51 // The charset using which {@link VCardInterpreter} parses the text. 52 // Each String is first decoded into binary stream with this charset, and encoded back 53 // to "target charset", which may be explicitly specified by the vCard with "CHARSET" 54 // property or implicitly mentioned by its version (e.g. vCard 3.0 recommends UTF-8). 55 private final String mSourceCharset; 56 57 private final boolean mStrictLineBreaking; 58 private final int mVCardType; 59 private final Account mAccount; 60 61 // For measuring performance. 62 private long mTimePushIntoContentResolver; 63 64 private final List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>(); 65 66 public VCardEntryConstructor() { 67 this(VCardConfig.VCARD_TYPE_V21_GENERIC, null); 68 } 69 70 public VCardEntryConstructor(final int vcardType) { 71 this(vcardType, null, null, false); 72 } 73 74 public VCardEntryConstructor(final int vcardType, final Account account) { 75 this(vcardType, account, null, false); 76 } 77 78 public VCardEntryConstructor(final int vcardType, final Account account, 79 final String inputCharset) { 80 this(vcardType, account, inputCharset, false); 81 } 82 83 /** 84 * @hide Just for testing. 85 */ 86 public VCardEntryConstructor(final int vcardType, final Account account, 87 final String inputCharset, final boolean strictLineBreakParsing) { 88 if (inputCharset != null) { 89 mSourceCharset = inputCharset; 90 } else { 91 mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; 92 } 93 mStrictLineBreaking = strictLineBreakParsing; 94 mVCardType = vcardType; 95 mAccount = account; 96 } 97 98 public void addEntryHandler(VCardEntryHandler entryHandler) { 99 mEntryHandlers.add(entryHandler); 100 } 101 102 @Override 103 public void start() { 104 for (VCardEntryHandler entryHandler : mEntryHandlers) { 105 entryHandler.onStart(); 106 } 107 } 108 109 @Override 110 public void end() { 111 for (VCardEntryHandler entryHandler : mEntryHandlers) { 112 entryHandler.onEnd(); 113 } 114 } 115 116 public void clear() { 117 mCurrentVCardEntry = null; 118 mCurrentProperty = new VCardEntry.Property(); 119 } 120 121 @Override 122 public void startEntry() { 123 if (mCurrentVCardEntry != null) { 124 Log.e(LOG_TAG, "Nested VCard code is not supported now."); 125 } 126 mCurrentVCardEntry = new VCardEntry(mVCardType, mAccount); 127 } 128 129 @Override 130 public void endEntry() { 131 mCurrentVCardEntry.consolidateFields(); 132 for (VCardEntryHandler entryHandler : mEntryHandlers) { 133 entryHandler.onEntryCreated(mCurrentVCardEntry); 134 } 135 mCurrentVCardEntry = null; 136 } 137 138 @Override 139 public void startProperty() { 140 mCurrentProperty.clear(); 141 } 142 143 @Override 144 public void endProperty() { 145 mCurrentVCardEntry.addProperty(mCurrentProperty); 146 } 147 148 @Override 149 public void propertyName(String name) { 150 mCurrentProperty.setPropertyName(name); 151 } 152 153 @Override 154 public void propertyGroup(String group) { 155 } 156 157 @Override 158 public void propertyParamType(String type) { 159 if (mParamType != null) { 160 Log.e(LOG_TAG, "propertyParamType() is called more than once " + 161 "before propertyParamValue() is called"); 162 } 163 mParamType = type; 164 } 165 166 @Override 167 public void propertyParamValue(String value) { 168 if (mParamType == null) { 169 // From vCard 2.1 specification. vCard 3.0 formally does not allow this case. 170 mParamType = "TYPE"; 171 } 172 if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) { 173 value = VCardUtils.convertStringCharset( 174 value, mSourceCharset, VCardConfig.DEFAULT_IMPORT_CHARSET); 175 } 176 mCurrentProperty.addParameter(mParamType, value); 177 mParamType = null; 178 } 179 180 private String handleOneValue(String value, 181 String sourceCharset, String targetCharset, String encoding) { 182 // It is possible when some of multiple values are empty. 183 // e.g. N:;a;;; -> values are "", "a", "", "", and "". 184 if (TextUtils.isEmpty(value)) { 185 return ""; 186 } 187 188 if (encoding != null) { 189 if (encoding.equals("BASE64") || encoding.equals("B")) { 190 mCurrentProperty.setPropertyBytes(Base64.decode(value.getBytes(), Base64.DEFAULT)); 191 return value; 192 } else if (encoding.equals("QUOTED-PRINTABLE")) { 193 return VCardUtils.parseQuotedPrintable( 194 value, mStrictLineBreaking, sourceCharset, targetCharset); 195 } 196 Log.w(LOG_TAG, "Unknown encoding. Fall back to default."); 197 } 198 199 // Just translate the charset of a given String from inputCharset to a system one. 200 return VCardUtils.convertStringCharset(value, sourceCharset, targetCharset); 201 } 202 203 @Override 204 public void propertyValues(List<String> values) { 205 if (values == null || values.isEmpty()) { 206 return; 207 } 208 209 final Collection<String> charsetCollection = 210 mCurrentProperty.getParameters(VCardConstants.PARAM_CHARSET); 211 final Collection<String> encodingCollection = 212 mCurrentProperty.getParameters(VCardConstants.PARAM_ENCODING); 213 final String encoding = 214 ((encodingCollection != null) ? encodingCollection.iterator().next() : null); 215 216 // String targetCharset = CharsetUtils.nameForDefaultVendor( 217 // ((charsetCollection != null) ? charsetCollection.iterator().next() : null)); 218 String targetCharset = 219 ((charsetCollection != null) ? charsetCollection.iterator().next() : null); 220 if (TextUtils.isEmpty(targetCharset)) { 221 targetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET; 222 } 223 224 for (final String value : values) { 225 mCurrentProperty.addToPropertyValueList( 226 handleOneValue(value, mSourceCharset, targetCharset, encoding)); 227 } 228 } 229 230 /** 231 * @hide 232 */ 233 public void showPerformanceInfo() { 234 Log.d(LOG_TAG, "time for insert ContactStruct to database: " + 235 mTimePushIntoContentResolver + " ms"); 236 } 237} 238