diff options
Diffstat (limited to 'vcard/java/com/android/vcard/VCardEntryConstructor.java')
-rw-r--r-- | vcard/java/com/android/vcard/VCardEntryConstructor.java | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/vcard/java/com/android/vcard/VCardEntryConstructor.java b/vcard/java/com/android/vcard/VCardEntryConstructor.java new file mode 100644 index 0000000..2679e23 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardEntryConstructor.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.vcard; + +import android.accounts.Account; +import android.text.TextUtils; +import android.util.Base64; +import android.util.CharsetUtils; +import android.util.Log; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * <p> + * The {@link VCardInterpreter} implementation which enables {@link VCardEntryHandler} objects + * to easily handle each vCard entry. + * </p> + * <p> + * This class understand details inside vCard and translates it to {@link VCardEntry}. + * Then the class throw it to {@link VCardEntryHandler} registered via + * {@link #addEntryHandler(VCardEntryHandler)}, so that all those registered objects + * are able to handle the {@link VCardEntry} object. + * </p> + * <p> + * If you want to know the detail inside vCard, it would be better to implement + * {@link VCardInterpreter} directly, instead of relying on this class and + * {@link VCardEntry} created by the object. + * </p> + */ +public class VCardEntryConstructor implements VCardInterpreter { + private static String LOG_TAG = "VCardEntryConstructor"; + + private VCardEntry.Property mCurrentProperty = new VCardEntry.Property(); + private VCardEntry mCurrentVCardEntry; + private String mParamType; + + // The charset using which {@link VCardInterpreter} parses the text. + // Each String is first decoded into binary stream with this charset, and encoded back + // to "target charset", which may be explicitly specified by the vCard with "CHARSET" + // property or implicitly mentioned by its version (e.g. vCard 3.0 recommends UTF-8). + private final String mSourceCharset; + + private final boolean mStrictLineBreaking; + private final int mVCardType; + private final Account mAccount; + + // For measuring performance. + private long mTimePushIntoContentResolver; + + private final List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>(); + + public VCardEntryConstructor() { + this(VCardConfig.VCARD_TYPE_V21_GENERIC, null, null, false); + } + + public VCardEntryConstructor(final int vcardType) { + this(vcardType, null, null, false); + } + + public VCardEntryConstructor(final int vcardType, final Account account) { + this(vcardType, account, null, false); + } + + public VCardEntryConstructor(final int vcardType, final Account account, + final String inputCharset) { + this(vcardType, account, inputCharset, false); + } + + /** + * @hide + */ + public VCardEntryConstructor(final int vcardType, final Account account, + final String inputCharset, final boolean strictLineBreakParsing) { + if (inputCharset != null) { + mSourceCharset = inputCharset; + } else { + mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; + } + mStrictLineBreaking = strictLineBreakParsing; + mVCardType = vcardType; + mAccount = account; + } + + public void addEntryHandler(VCardEntryHandler entryHandler) { + mEntryHandlers.add(entryHandler); + } + + public void start() { + for (VCardEntryHandler entryHandler : mEntryHandlers) { + entryHandler.onStart(); + } + } + + public void end() { + for (VCardEntryHandler entryHandler : mEntryHandlers) { + entryHandler.onEnd(); + } + } + + public void clear() { + mCurrentVCardEntry = null; + mCurrentProperty = new VCardEntry.Property(); + } + + public void startEntry() { + if (mCurrentVCardEntry != null) { + Log.e(LOG_TAG, "Nested VCard code is not supported now."); + } + mCurrentVCardEntry = new VCardEntry(mVCardType, mAccount); + } + + public void endEntry() { + mCurrentVCardEntry.consolidateFields(); + for (VCardEntryHandler entryHandler : mEntryHandlers) { + entryHandler.onEntryCreated(mCurrentVCardEntry); + } + mCurrentVCardEntry = null; + } + + public void startProperty() { + mCurrentProperty.clear(); + } + + public void endProperty() { + mCurrentVCardEntry.addProperty(mCurrentProperty); + } + + public void propertyName(String name) { + mCurrentProperty.setPropertyName(name); + } + + public void propertyGroup(String group) { + } + + public void propertyParamType(String type) { + if (mParamType != null) { + Log.e(LOG_TAG, "propertyParamType() is called more than once " + + "before propertyParamValue() is called"); + } + mParamType = type; + } + + public void propertyParamValue(String value) { + if (mParamType == null) { + // From vCard 2.1 specification. vCard 3.0 formally does not allow this case. + mParamType = "TYPE"; + } + mCurrentProperty.addParameter(mParamType, value); + mParamType = null; + } + + private static String encodeToSystemCharset(String originalString, + String sourceCharset, String targetCharset) { + if (sourceCharset.equalsIgnoreCase(targetCharset)) { + return originalString; + } + final Charset charset = Charset.forName(sourceCharset); + final ByteBuffer byteBuffer = charset.encode(originalString); + // byteBuffer.array() "may" return byte array which is larger than + // byteBuffer.remaining(). Here, we keep on the safe side. + final byte[] bytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(bytes); + try { + String ret = new String(bytes, targetCharset); + return ret; + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return null; + } + } + + private String handleOneValue(String value, + String sourceCharset, String targetCharset, String encoding) { + if (value == null) { + Log.w(LOG_TAG, "Null is given."); + value = ""; + } + + if (encoding != null) { + if (encoding.equals("BASE64") || encoding.equals("B")) { + mCurrentProperty.setPropertyBytes(Base64.decode(value.getBytes(), Base64.DEFAULT)); + return value; + } else if (encoding.equals("QUOTED-PRINTABLE")) { + return VCardUtils.parseQuotedPrintable( + value, mStrictLineBreaking, sourceCharset, targetCharset); + } + Log.w(LOG_TAG, "Unknown encoding. Fall back to default."); + } + + // Just translate the charset of a given String from inputCharset to a system one. + return encodeToSystemCharset(value, sourceCharset, targetCharset); + } + + public void propertyValues(List<String> values) { + if (values == null || values.isEmpty()) { + return; + } + + final Collection<String> charsetCollection = mCurrentProperty.getParameters("CHARSET"); + final Collection<String> encodingCollection = mCurrentProperty.getParameters("ENCODING"); + final String encoding = + ((encodingCollection != null) ? encodingCollection.iterator().next() : null); + String targetCharset = CharsetUtils.nameForDefaultVendor( + ((charsetCollection != null) ? charsetCollection.iterator().next() : null)); + if (TextUtils.isEmpty(targetCharset)) { + targetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET; + } + + for (final String value : values) { + mCurrentProperty.addToPropertyValueList( + handleOneValue(value, mSourceCharset, targetCharset, encoding)); + } + } + + /** + * @hide + */ + public void showPerformanceInfo() { + Log.d(LOG_TAG, "time for insert ContactStruct to database: " + + mTimePushIntoContentResolver + " ms"); + } +} |