Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 347daf3a authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change I2a52a60a into eclair-mr2

* changes:
  Implement unit tests for vCard exporter, which depends on the sucess in vCard importer.
parents 1cf6c2c3 ba2b21be
Loading
Loading
Loading
Loading
+46 −23
Original line number Original line Diff line number Diff line
@@ -111,6 +111,10 @@ public class VCardComposer {
    public static final String FAILURE_REASON_NOT_INITIALIZED =
    public static final String FAILURE_REASON_NOT_INITIALIZED =
        "The vCard composer object is not correctly initialized";
        "The vCard composer object is not correctly initialized";


    /** Should be visible only from developers... (no need to translate, hopefully) */
    public static final String FAILURE_REASON_UNSUPPORTED_URI =
        "The Uri vCard composer received is not supported by the composer.";

    public static final String NO_ERROR = "No error";
    public static final String NO_ERROR = "No error";


    public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
    public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
@@ -138,6 +142,15 @@ public class VCardComposer {


    private static final String SHIFT_JIS = "SHIFT_JIS";
    private static final String SHIFT_JIS = "SHIFT_JIS";


    /**
     * Special URI for testing.
     */
    public static final String VCARD_TEST_AUTHORITY = "com.android.unit_tests.vcard";
    public static final Uri VCARD_TEST_AUTHORITY_URI =
        Uri.parse("content://" + VCARD_TEST_AUTHORITY);
    public static final Uri CONTACTS_TEST_CONTENT_URI =
        Uri.withAppendedPath(VCARD_TEST_AUTHORITY_URI, "contacts");

    private static final Uri sDataRequestUri;
    private static final Uri sDataRequestUri;
    private static final Map<Integer, String> sImMap;
    private static final Map<Integer, String> sImMap;


@@ -286,7 +299,7 @@ public class VCardComposer {


    private String mErrorReason = NO_ERROR;
    private String mErrorReason = NO_ERROR;


    private boolean mIsCallLogComposer = false;
    private boolean mIsCallLogComposer;


    private static final String[] sContactsProjection = new String[] {
    private static final String[] sContactsProjection = new String[] {
        Contacts._ID,
        Contacts._ID,
@@ -307,30 +320,24 @@ public class VCardComposer {
    private static final String FLAG_TIMEZONE_UTC = "Z";
    private static final String FLAG_TIMEZONE_UTC = "Z";


    public VCardComposer(Context context) {
    public VCardComposer(Context context) {
        this(context, VCardConfig.VCARD_TYPE_DEFAULT, true, false);
        this(context, VCardConfig.VCARD_TYPE_DEFAULT, true);
    }
    }


    public VCardComposer(Context context, String vcardTypeStr,
    public VCardComposer(Context context, int vcardType) {
            boolean careHandlerErrors) {
        this(context, vcardType, true);
        this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr),
                careHandlerErrors, false);
    }
    }


    public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) {
    public VCardComposer(Context context, String vcardTypeStr, boolean careHandlerErrors) {
        this(context, vcardType, careHandlerErrors, false);
        this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr), careHandlerErrors);
    }
    }


    /**
    /**
     * Construct for supporting call log entry vCard composing.
     * Construct for supporting call log entry vCard composing.
     *
     * @param isCallLogComposer true if this composer is for creating Call Log vCard.
     */
     */
    public VCardComposer(Context context, int vcardType, boolean careHandlerErrors,
    public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) {
            boolean isCallLogComposer) {
        mContext = context;
        mContext = context;
        mVCardType = vcardType;
        mVCardType = vcardType;
        mCareHandlerErrors = careHandlerErrors;
        mCareHandlerErrors = careHandlerErrors;
        mIsCallLogComposer = isCallLogComposer;
        mContentResolver = context.getContentResolver();
        mContentResolver = context.getContentResolver();


        mIsV30 = VCardConfig.isV30(vcardType);
        mIsV30 = VCardConfig.isV30(vcardType);
@@ -370,15 +377,26 @@ public class VCardComposer {
        mHandlerList.add(handler);
        mHandlerList.add(handler);
    }
    }


    /**
     * @return Returns true when initialization is successful and all the other
     *          methods are available. Returns false otherwise.
     */
    public boolean init() {
    public boolean init() {
        return init(null, null);
        return init(null, null);
    }
    }


    public boolean init(final String selection, final String[] selectionArgs) {
        return init(Contacts.CONTENT_URI, selection, selectionArgs, null);
    }

    /**
    /**
     * @return Returns true when initialization is successful and all the other
     * Note that this is unstable interface, may be deleted in the future.
     *          methods are available. Returns false otherwise.
     */
     */
    public boolean init(final String selection, final String[] selectionArgs) {
    public boolean init(final Uri contentUri, final String selection,
            final String[] selectionArgs, final String sortOrder) {
        if (contentUri == null) {
            return false;
        }
        if (mCareHandlerErrors) {
        if (mCareHandlerErrors) {
            List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
            List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
                    mHandlerList.size());
                    mHandlerList.size());
@@ -397,13 +415,19 @@ public class VCardComposer {
            }
            }
        }
        }


        if (mIsCallLogComposer) {
        final String[] projection;
            mCursor = mContentResolver.query(CallLog.Calls.CONTENT_URI, sCallLogProjection,
        if (CallLog.Calls.CONTENT_URI.equals(contentUri)) {
                    selection, selectionArgs, null);
            projection = sCallLogProjection;
            mIsCallLogComposer = true;
        } else if (Contacts.CONTENT_URI.equals(contentUri) ||
                CONTACTS_TEST_CONTENT_URI.equals(contentUri)) {
            projection = sContactsProjection;
        } else {
        } else {
            mCursor = mContentResolver.query(Contacts.CONTENT_URI, sContactsProjection,
            mErrorReason = FAILURE_REASON_UNSUPPORTED_URI;
                    selection, selectionArgs, null);
            return false;
        }
        }
        mCursor = mContentResolver.query(
                contentUri, projection, selection, selectionArgs, sortOrder);


        if (mCursor == null) {
        if (mCursor == null) {
            mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO;
            mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO;
@@ -496,8 +520,7 @@ public class VCardComposer {
            dataExists = entityIterator.hasNext();
            dataExists = entityIterator.hasNext();
            while (entityIterator.hasNext()) {
            while (entityIterator.hasNext()) {
                Entity entity = entityIterator.next();
                Entity entity = entityIterator.next();
                for (NamedContentValues namedContentValues : entity
                for (NamedContentValues namedContentValues : entity.getSubValues()) {
                        .getSubValues()) {
                    ContentValues contentValues = namedContentValues.values;
                    ContentValues contentValues = namedContentValues.values;
                    String key = contentValues.getAsString(Data.MIMETYPE);
                    String key = contentValues.getAsString(Data.MIMETYPE);
                    if (key != null) {
                    if (key != null) {
+12 −1
Original line number Original line Diff line number Diff line
@@ -525,10 +525,18 @@ public class VCardParser_V21 extends VCardParser {
                throw new VCardException("Unknown type \"" + paramName + "\"");
                throw new VCardException("Unknown type \"" + paramName + "\"");
            }
            }
        } else {
        } else {
            handleType(strArray[0]);
            handleParamWithoutName(strArray[0]);
        }
        }
    }
    }
    
    
    /**
     * vCard 3.0 parser may throw VCardException.
     */
    @SuppressWarnings("unused")
    protected void handleParamWithoutName(final String paramValue) throws VCardException {
        handleType(paramValue);
    }

    /**
    /**
     * ptypeval = knowntype / "X-" word
     * ptypeval = knowntype / "X-" word
     */
     */
@@ -832,6 +840,9 @@ public class VCardParser_V21 extends VCardParser {
    @Override
    @Override
    public boolean parse(InputStream is, String charset, VCardBuilder builder)
    public boolean parse(InputStream is, String charset, VCardBuilder builder)
            throws IOException, VCardException {
            throws IOException, VCardException {
        if (charset == null) {
            charset = VCardConfig.DEFAULT_CHARSET;
        }
        final InputStreamReader tmpReader = new InputStreamReader(is, charset);
        final InputStreamReader tmpReader = new InputStreamReader(is, charset);
        if (VCardConfig.showPerformanceLog()) {
        if (VCardConfig.showPerformanceLog()) {
            mReader = new CustomBufferedReader(tmpReader);
            mReader = new CustomBufferedReader(tmpReader);
+34 −2
Original line number Original line Diff line number Diff line
@@ -49,6 +49,29 @@ public class VCardParser_V30 extends VCardParser_V21 {


    private boolean mEmittedAgentWarning = false;
    private boolean mEmittedAgentWarning = false;


    /**
     * True when the caller wants the parser to be strict about the input.
     * Currently this is only for testing.
     */
    private final boolean mStrictParsing;

    public VCardParser_V30() {
        super();
        mStrictParsing = false;
    }

    /**
     * @param strictParsing when true, this object throws VCardException when the vcard is not
     * valid from the view of vCard 3.0 specification (defined in RFC 2426). Note that this class
     * is not fully yet for being used with this flag and may not notice invalid line(s).
     *
     * @hide currently only for testing! 
     */
    public VCardParser_V30(boolean strictParsing) {
        super();
        mStrictParsing = strictParsing;
    }

    @Override
    @Override
    protected int getVersion() {
    protected int getVersion() {
        return VCardConfig.FLAG_V30;
        return VCardConfig.FLAG_V30;
@@ -205,6 +228,15 @@ public class VCardParser_V30 extends VCardParser_V21 {
        super.handleAnyParam(paramName, paramValue);
        super.handleAnyParam(paramName, paramValue);
    }
    }


    @Override
    protected void handleParamWithoutName(final String paramValue) throws VCardException {
        if (mStrictParsing) {
            throw new VCardException("Parameter without name is not acceptable in vCard 3.0");
        } else {
            super.handleParamWithoutName(paramValue);
        }
    }

    /**
    /**
     *  vCard 3.0 defines
     *  vCard 3.0 defines
     *  
     *  
+254 −0
Original line number Original line Diff line number Diff line
/*
 * 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.unit_tests.vcard;

import android.content.ContentResolver;
import android.database.CharArrayBuffer;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Bundle;

import java.util.Map;

public class MockCursor implements Cursor {
    public int getColumnCount() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public int getColumnIndex(String columnName) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public int getColumnIndexOrThrow(String columnName) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public String getColumnName(int columnIndex) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public String[] getColumnNames() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public int getCount() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean isNull(int columnIndex) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public int getInt(int columnIndex) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public long getLong(int columnIndex) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public short getShort(int columnIndex) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public float getFloat(int columnIndex) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public double getDouble(int columnIndex) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public byte[] getBlob(int columnIndex) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public String getString(int columnIndex) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public Bundle getExtras() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public int getPosition() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean isAfterLast() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean isBeforeFirst() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean isFirst() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean isLast() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean move(int offset) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean moveToFirst() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean moveToLast() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean moveToNext() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean moveToPrevious() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean moveToPosition(int position) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public void deactivate() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public void close() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean isClosed() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean requery() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public void registerContentObserver(ContentObserver observer) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public void registerDataSetObserver(DataSetObserver observer) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public Bundle respond(Bundle extras) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    public boolean getWantsAllOnMoveCalls() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean commitUpdates() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean commitUpdates(Map<? extends Long, ? extends Map<String, Object>> values) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean hasUpdates() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public void setNotificationUri(ContentResolver cr, Uri uri) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean supportsUpdates() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean deleteRow() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public void unregisterContentObserver(ContentObserver observer) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public void unregisterDataSetObserver(DataSetObserver observer) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean updateBlob(int columnIndex, byte[] value) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean updateDouble(int columnIndex, double value) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean updateFloat(int columnIndex, float value) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean updateInt(int columnIndex, int value) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean updateLong(int columnIndex, long value) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean updateShort(int columnIndex, short value) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean updateString(int columnIndex, String value) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public boolean updateToNull(int columnIndex) {
        throw new UnsupportedOperationException("unimplemented mock method");
    }

    @SuppressWarnings("deprecation")
    public void abortUpdates() {
        throw new UnsupportedOperationException("unimplemented mock method");
    }
}
 No newline at end of file
+9 −164
Original line number Original line Diff line number Diff line
@@ -18,15 +18,11 @@ package com.android.unit_tests.vcard;
import android.content.ContentValues;
import android.content.ContentValues;
import android.pim.vcard.ContactStruct;
import android.pim.vcard.ContactStruct;


import org.apache.commons.codec.binary.Base64;

import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.HashSet;
import java.util.HashSet;
import java.util.List;
import java.util.List;
import java.util.Set;
import java.util.Set;
import java.util.Map.Entry;
import java.util.regex.Pattern;


/**
/**
 * Previously used in main vCard handling code but now exists only for testing.
 * Previously used in main vCard handling code but now exists only for testing.
@@ -106,6 +102,15 @@ public class PropertyNode {
        }
        }
    }
    }
    
    
    @Override
    public int hashCode() {
        // vCard may contain more than one same line in one entry, while HashSet or any other
        // library which utilize hashCode() does not honor that, so intentionally throw an
        // Exception.
        throw new UnsupportedOperationException(
                "PropertyNode does not provide hashCode() implementation intentionally.");
    }

    @Override
    @Override
    public boolean equals(Object obj) {
    public boolean equals(Object obj) {
        if (!(obj instanceof PropertyNode)) {
        if (!(obj instanceof PropertyNode)) {
@@ -165,164 +170,4 @@ public class PropertyNode {
        builder.append(propValue);
        builder.append(propValue);
        return builder.toString();
        return builder.toString();
    }
    }
    
    /**
     * Encode this object into a string which can be decoded. 
     */
    public String encode() {
        // PropertyNode#toString() is for reading, not for parsing in the future.
        // We construct appropriate String here.
        StringBuilder builder = new StringBuilder();
        if (propName.length() > 0) {
            builder.append("propName:[");
            builder.append(propName);
            builder.append("],");
        }
        int size = propGroupSet.size();
        if (size > 0) {
            Set<String> set = propGroupSet;
            builder.append("propGroup:[");
            int i = 0;
            for (String group : set) {
                // We do not need to double quote groups.
                // group        = 1*(ALPHA / DIGIT / "-")
                builder.append(group);
                if (i < size - 1) {
                    builder.append(",");
                }
                i++;
            }
            builder.append("],");
        }

        if (paramMap.size() > 0 || paramMap_TYPE.size() > 0) {
            ContentValues values = paramMap;
            builder.append("paramMap:[");
            size = paramMap.size(); 
            int i = 0;
            for (Entry<String, Object> entry : values.valueSet()) {
                // Assuming param-key does not contain NON-ASCII nor symbols.
                //
                // According to vCard 3.0:
                // param-name   = iana-token / x-name
                builder.append(entry.getKey());

                // param-value may contain any value including NON-ASCIIs.
                // We use the following replacing rule.
                // \ -> \\
                // , -> \,
                // In String#replaceAll(), "\\\\" means a single backslash.
                builder.append("=");
                builder.append(entry.getValue().toString()
                        .replaceAll("\\\\", "\\\\\\\\")
                        .replaceAll(",", "\\\\,"));
                if (i < size -1) {
                    builder.append(",");
                }
                i++;
            }

            Set<String> set = paramMap_TYPE;
            size = paramMap_TYPE.size();
            if (i > 0 && size > 0) {
                builder.append(",");
            }
            i = 0;
            for (String type : set) {
                builder.append("TYPE=");
                builder.append(type
                        .replaceAll("\\\\", "\\\\\\\\")
                        .replaceAll(",", "\\\\,"));
                if (i < size - 1) {
                    builder.append(",");
                }
                i++;
            }
            builder.append("],");
        }

        size = propValue_vector.size();
        if (size > 0) {
            builder.append("propValue:[");
            List<String> list = propValue_vector;
            for (int i = 0; i < size; i++) {
                builder.append(list.get(i)
                        .replaceAll("\\\\", "\\\\\\\\")
                        .replaceAll(",", "\\\\,"));
                if (i < size -1) {
                    builder.append(",");
                }
            }
            builder.append("],");
        }

        return builder.toString();
    }

    public static PropertyNode decode(String encodedString) {
        PropertyNode propertyNode = new PropertyNode();
        String trimed = encodedString.trim();
        if (trimed.length() == 0) {
            return propertyNode;
        }
        String[] elems = trimed.split("],");
        
        for (String elem : elems) {
            int index = elem.indexOf('[');
            String name = elem.substring(0, index - 1);
            Pattern pattern = Pattern.compile("(?<!\\\\),");
            String[] values = pattern.split(elem.substring(index + 1), -1);
            if (name.equals("propName")) {
                propertyNode.propName = values[0];
            } else if (name.equals("propGroupSet")) {
                for (String value : values) {
                    propertyNode.propGroupSet.add(value);
                }
            } else if (name.equals("paramMap")) {
                ContentValues paramMap = propertyNode.paramMap;
                Set<String> paramMap_TYPE = propertyNode.paramMap_TYPE;
                for (String value : values) {
                    String[] tmp = value.split("=", 2);
                    String mapKey = tmp[0];
                    // \, -> ,
                    // \\ -> \
                    // In String#replaceAll(), "\\\\" means a single backslash.
                    String mapValue =
                        tmp[1].replaceAll("\\\\,", ",").replaceAll("\\\\\\\\", "\\\\");
                    if (mapKey.equalsIgnoreCase("TYPE")) {
                        paramMap_TYPE.add(mapValue);
                    } else {
                        paramMap.put(mapKey, mapValue);
                    }
                }
            } else if (name.equals("propValue")) {
                StringBuilder builder = new StringBuilder();
                List<String> list = propertyNode.propValue_vector;
                int length = values.length;
                for (int i = 0; i < length; i++) {
                    String normValue = values[i]
                                              .replaceAll("\\\\,", ",")
                                              .replaceAll("\\\\\\\\", "\\\\");
                    list.add(normValue);
                    builder.append(normValue);
                    if (i < length - 1) {
                        builder.append(";");
                    }
                }
                propertyNode.propValue = builder.toString();
            }
        }
        
        // At this time, QUOTED-PRINTABLE is already decoded to Java String.
        // We just need to decode BASE64 String to binary.
        String encoding = propertyNode.paramMap.getAsString("ENCODING");
        if (encoding != null &&
                (encoding.equalsIgnoreCase("BASE64") ||
                        encoding.equalsIgnoreCase("B"))) {
            propertyNode.propValue_bytes =
                Base64.decodeBase64(propertyNode.propValue_vector.get(0).getBytes());
        }
        
        return propertyNode;
    }
}
}
Loading