Loading core/java/android/content/AbstractSyncableContentProvider.java +121 −49 Original line number Diff line number Diff line Loading @@ -147,7 +147,8 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro @Override public boolean onCreate() { if (isTemporary()) throw new IllegalStateException("onCreate() called for temp provider"); mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(), mDatabaseName); mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(), mDatabaseName); mSyncState = new SyncStateContentProviderHelper(mOpenHelper); AccountMonitorListener listener = new AccountMonitorListener() { Loading Loading @@ -235,76 +236,147 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro return Collections.emptyList(); } @Override public final int update(final Uri url, final ContentValues values, final String selection, final String[] selectionArgs) { /** * <p> * Call mOpenHelper.getWritableDatabase() and mDb.beginTransaction(). * {@link #endTransaction} MUST be called after calling this method. * Those methods should be used like this: * </p> * * <pre class="prettyprint"> * boolean successful = false; * beginTransaction(); * try { * // Do something related to mDb * successful = true; * return ret; * } finally { * endTransaction(successful); * } * </pre> * * @hide This method is dangerous from the view of database manipulation, though using * this makes batch insertion/update/delete much faster. */ public final void beginTransaction() { mDb = mOpenHelper.getWritableDatabase(); mDb.beginTransaction(); } /** * <p> * Call mDb.endTransaction(). If successful is true, try to call * mDb.setTransactionSuccessful() before calling mDb.endTransaction(). * This method MUST be used with {@link #beginTransaction()}. * </p> * * @hide This method is dangerous from the view of database manipulation, though using * this makes batch insertion/update/delete much faster. */ public final void endTransaction(boolean successful) { try { if (isTemporary() && mSyncState.matches(url)) { int numRows = mSyncState.asContentProvider().update( url, values, selection, selectionArgs); if (successful) { // setTransactionSuccessful() must be called just once during opening the // transaction. mDb.setTransactionSuccessful(); return numRows; } } finally { mDb.endTransaction(); } } int result = updateInternal(url, values, selection, selectionArgs); mDb.setTransactionSuccessful(); @Override public final int update(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) { boolean successful = false; beginTransaction(); try { int ret = nonTransactionalUpdate(uri, values, selection, selectionArgs); successful = true; return ret; } finally { endTransaction(successful); } } /** * @hide */ public final int nonTransactionalUpdate(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) { if (isTemporary() && mSyncState.matches(uri)) { int numRows = mSyncState.asContentProvider().update( uri, values, selection, selectionArgs); return numRows; } int result = updateInternal(uri, values, selection, selectionArgs); if (!isTemporary() && result > 0) { getContext().getContentResolver().notifyChange(url, null /* observer */, changeRequiresLocalSync(url)); getContext().getContentResolver().notifyChange(uri, null /* observer */, changeRequiresLocalSync(uri)); } return result; } finally { mDb.endTransaction(); } } @Override public final int delete(final Uri url, final String selection, public final int delete(final Uri uri, final String selection, final String[] selectionArgs) { mDb = mOpenHelper.getWritableDatabase(); mDb.beginTransaction(); boolean successful = false; beginTransaction(); try { if (isTemporary() && mSyncState.matches(url)) { int numRows = mSyncState.asContentProvider().delete(url, selection, selectionArgs); mDb.setTransactionSuccessful(); int ret = nonTransactionalDelete(uri, selection, selectionArgs); successful = true; return ret; } finally { endTransaction(successful); } } /** * @hide */ public final int nonTransactionalDelete(final Uri uri, final String selection, final String[] selectionArgs) { if (isTemporary() && mSyncState.matches(uri)) { int numRows = mSyncState.asContentProvider().delete(uri, selection, selectionArgs); return numRows; } int result = deleteInternal(url, selection, selectionArgs); mDb.setTransactionSuccessful(); int result = deleteInternal(uri, selection, selectionArgs); if (!isTemporary() && result > 0) { getContext().getContentResolver().notifyChange(url, null /* observer */, changeRequiresLocalSync(url)); getContext().getContentResolver().notifyChange(uri, null /* observer */, changeRequiresLocalSync(uri)); } return result; } finally { mDb.endTransaction(); } } @Override public final Uri insert(final Uri url, final ContentValues values) { mDb = mOpenHelper.getWritableDatabase(); mDb.beginTransaction(); public final Uri insert(final Uri uri, final ContentValues values) { boolean successful = false; beginTransaction(); try { if (isTemporary() && mSyncState.matches(url)) { Uri result = mSyncState.asContentProvider().insert(url, values); mDb.setTransactionSuccessful(); Uri ret = nonTransactionalInsert(uri, values); successful = true; return ret; } finally { endTransaction(successful); } } /** * @hide */ public final Uri nonTransactionalInsert(final Uri uri, final ContentValues values) { if (isTemporary() && mSyncState.matches(uri)) { Uri result = mSyncState.asContentProvider().insert(uri, values); return result; } Uri result = insertInternal(url, values); mDb.setTransactionSuccessful(); Uri result = insertInternal(uri, values); if (!isTemporary() && result != null) { getContext().getContentResolver().notifyChange(url, null /* observer */, changeRequiresLocalSync(url)); getContext().getContentResolver().notifyChange(uri, null /* observer */, changeRequiresLocalSync(uri)); } return result; } finally { mDb.endTransaction(); } } @Override Loading core/java/android/provider/Contacts.java +18 −1 Original line number Diff line number Diff line Loading @@ -868,6 +868,17 @@ public class Contacts { public static final int TYPE_WORK = 2; public static final int TYPE_OTHER = 3; /** * @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future. */ public static final int MOBILE_EMAIL_TYPE_INDEX = 2; /** * @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future. * This is not "mobile" but "CELL" since vCard uses it for identifying mobile phone. */ public static final String MOBILE_EMAIL_TYPE_NAME = "_AUTO_CELL"; /** * The user defined label for the the contact method. * <P>Type: TEXT</P> Loading Loading @@ -1005,9 +1016,15 @@ public class Contacts { } } else { if (!TextUtils.isEmpty(label)) { if (label.toString().equals(MOBILE_EMAIL_TYPE_NAME)) { display = context.getString( com.android.internal.R.string.mobileEmailTypeName); } else { display = label; } } } break; } Loading core/java/android/syncml/pim/PropertyNode.java +180 −14 Original line number Diff line number Diff line Loading @@ -17,12 +17,16 @@ package android.syncml.pim; import android.content.ContentValues; import android.util.Log; import org.apache.commons.codec.binary.Base64; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Map.Entry; import java.util.regex.Pattern; public class PropertyNode { Loading Loading @@ -52,7 +56,9 @@ public class PropertyNode { public Set<String> propGroupSet; public PropertyNode() { propName = ""; propValue = ""; propValue_vector = new ArrayList<String>(); paramMap = new ContentValues(); paramMap_TYPE = new HashSet<String>(); propGroupSet = new HashSet<String>(); Loading @@ -62,13 +68,21 @@ public class PropertyNode { String propName, String propValue, List<String> propValue_vector, byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE, Set<String> propGroupSet) { if (propName != null) { this.propName = propName; } else { this.propName = ""; } if (propValue != null) { this.propValue = propValue; } else { this.propValue = ""; } if (propValue_vector != null) { this.propValue_vector = propValue_vector; } else { this.propValue_vector = new ArrayList<String>(); } this.propValue_bytes = propValue_bytes; if (paramMap != null) { this.paramMap = paramMap; Loading Loading @@ -117,17 +131,9 @@ public class PropertyNode { // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector // is 1, the encoded value is stored in propValue, so we do not have to // check it. if (propValue_vector != null) { // Log.d("@@@", "===" + propValue_vector + ", " + node.propValue_vector); return (propValue_vector.equals(node.propValue_vector) || (propValue_vector.size() == 1)); } else if (node.propValue_vector != null) { // Log.d("@@@", "===" + propValue_vector + ", " + node.propValue_vector); return (node.propValue_vector.equals(propValue_vector) || (node.propValue_vector.size() == 1)); } else { return true; } propValue_vector.size() == 1 || node.propValue_vector.size() == 1); } } Loading @@ -154,4 +160,164 @@ public class PropertyNode { builder.append(propValue); 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; } } core/java/android/syncml/pim/VBuilderCollection.java 0 → 100644 +100 −0 Original line number 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 android.syncml.pim; import java.util.Collection; import java.util.List; public class VBuilderCollection implements VBuilder { private final Collection<VBuilder> mVBuilderCollection; public VBuilderCollection(Collection<VBuilder> vBuilderCollection) { mVBuilderCollection = vBuilderCollection; } public Collection<VBuilder> getVBuilderCollection() { return mVBuilderCollection; } public void start() { for (VBuilder builder : mVBuilderCollection) { builder.start(); } } public void end() { for (VBuilder builder : mVBuilderCollection) { builder.end(); } } public void startRecord(String type) { for (VBuilder builder : mVBuilderCollection) { builder.startRecord(type); } } public void endRecord() { for (VBuilder builder : mVBuilderCollection) { builder.endRecord(); } } public void startProperty() { for (VBuilder builder : mVBuilderCollection) { builder.startProperty(); } } public void endProperty() { for (VBuilder builder : mVBuilderCollection) { builder.endProperty(); } } public void propertyGroup(String group) { for (VBuilder builder : mVBuilderCollection) { builder.propertyGroup(group); } } public void propertyName(String name) { for (VBuilder builder : mVBuilderCollection) { builder.propertyName(name); } } public void propertyParamType(String type) { for (VBuilder builder : mVBuilderCollection) { builder.propertyParamType(type); } } public void propertyParamValue(String value) { for (VBuilder builder : mVBuilderCollection) { builder.propertyParamValue(value); } } public void propertyValues(List<String> values) { for (VBuilder builder : mVBuilderCollection) { builder.propertyValues(values); } } } Loading
core/java/android/content/AbstractSyncableContentProvider.java +121 −49 Original line number Diff line number Diff line Loading @@ -147,7 +147,8 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro @Override public boolean onCreate() { if (isTemporary()) throw new IllegalStateException("onCreate() called for temp provider"); mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(), mDatabaseName); mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(), mDatabaseName); mSyncState = new SyncStateContentProviderHelper(mOpenHelper); AccountMonitorListener listener = new AccountMonitorListener() { Loading Loading @@ -235,76 +236,147 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro return Collections.emptyList(); } @Override public final int update(final Uri url, final ContentValues values, final String selection, final String[] selectionArgs) { /** * <p> * Call mOpenHelper.getWritableDatabase() and mDb.beginTransaction(). * {@link #endTransaction} MUST be called after calling this method. * Those methods should be used like this: * </p> * * <pre class="prettyprint"> * boolean successful = false; * beginTransaction(); * try { * // Do something related to mDb * successful = true; * return ret; * } finally { * endTransaction(successful); * } * </pre> * * @hide This method is dangerous from the view of database manipulation, though using * this makes batch insertion/update/delete much faster. */ public final void beginTransaction() { mDb = mOpenHelper.getWritableDatabase(); mDb.beginTransaction(); } /** * <p> * Call mDb.endTransaction(). If successful is true, try to call * mDb.setTransactionSuccessful() before calling mDb.endTransaction(). * This method MUST be used with {@link #beginTransaction()}. * </p> * * @hide This method is dangerous from the view of database manipulation, though using * this makes batch insertion/update/delete much faster. */ public final void endTransaction(boolean successful) { try { if (isTemporary() && mSyncState.matches(url)) { int numRows = mSyncState.asContentProvider().update( url, values, selection, selectionArgs); if (successful) { // setTransactionSuccessful() must be called just once during opening the // transaction. mDb.setTransactionSuccessful(); return numRows; } } finally { mDb.endTransaction(); } } int result = updateInternal(url, values, selection, selectionArgs); mDb.setTransactionSuccessful(); @Override public final int update(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) { boolean successful = false; beginTransaction(); try { int ret = nonTransactionalUpdate(uri, values, selection, selectionArgs); successful = true; return ret; } finally { endTransaction(successful); } } /** * @hide */ public final int nonTransactionalUpdate(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) { if (isTemporary() && mSyncState.matches(uri)) { int numRows = mSyncState.asContentProvider().update( uri, values, selection, selectionArgs); return numRows; } int result = updateInternal(uri, values, selection, selectionArgs); if (!isTemporary() && result > 0) { getContext().getContentResolver().notifyChange(url, null /* observer */, changeRequiresLocalSync(url)); getContext().getContentResolver().notifyChange(uri, null /* observer */, changeRequiresLocalSync(uri)); } return result; } finally { mDb.endTransaction(); } } @Override public final int delete(final Uri url, final String selection, public final int delete(final Uri uri, final String selection, final String[] selectionArgs) { mDb = mOpenHelper.getWritableDatabase(); mDb.beginTransaction(); boolean successful = false; beginTransaction(); try { if (isTemporary() && mSyncState.matches(url)) { int numRows = mSyncState.asContentProvider().delete(url, selection, selectionArgs); mDb.setTransactionSuccessful(); int ret = nonTransactionalDelete(uri, selection, selectionArgs); successful = true; return ret; } finally { endTransaction(successful); } } /** * @hide */ public final int nonTransactionalDelete(final Uri uri, final String selection, final String[] selectionArgs) { if (isTemporary() && mSyncState.matches(uri)) { int numRows = mSyncState.asContentProvider().delete(uri, selection, selectionArgs); return numRows; } int result = deleteInternal(url, selection, selectionArgs); mDb.setTransactionSuccessful(); int result = deleteInternal(uri, selection, selectionArgs); if (!isTemporary() && result > 0) { getContext().getContentResolver().notifyChange(url, null /* observer */, changeRequiresLocalSync(url)); getContext().getContentResolver().notifyChange(uri, null /* observer */, changeRequiresLocalSync(uri)); } return result; } finally { mDb.endTransaction(); } } @Override public final Uri insert(final Uri url, final ContentValues values) { mDb = mOpenHelper.getWritableDatabase(); mDb.beginTransaction(); public final Uri insert(final Uri uri, final ContentValues values) { boolean successful = false; beginTransaction(); try { if (isTemporary() && mSyncState.matches(url)) { Uri result = mSyncState.asContentProvider().insert(url, values); mDb.setTransactionSuccessful(); Uri ret = nonTransactionalInsert(uri, values); successful = true; return ret; } finally { endTransaction(successful); } } /** * @hide */ public final Uri nonTransactionalInsert(final Uri uri, final ContentValues values) { if (isTemporary() && mSyncState.matches(uri)) { Uri result = mSyncState.asContentProvider().insert(uri, values); return result; } Uri result = insertInternal(url, values); mDb.setTransactionSuccessful(); Uri result = insertInternal(uri, values); if (!isTemporary() && result != null) { getContext().getContentResolver().notifyChange(url, null /* observer */, changeRequiresLocalSync(url)); getContext().getContentResolver().notifyChange(uri, null /* observer */, changeRequiresLocalSync(uri)); } return result; } finally { mDb.endTransaction(); } } @Override Loading
core/java/android/provider/Contacts.java +18 −1 Original line number Diff line number Diff line Loading @@ -868,6 +868,17 @@ public class Contacts { public static final int TYPE_WORK = 2; public static final int TYPE_OTHER = 3; /** * @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future. */ public static final int MOBILE_EMAIL_TYPE_INDEX = 2; /** * @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future. * This is not "mobile" but "CELL" since vCard uses it for identifying mobile phone. */ public static final String MOBILE_EMAIL_TYPE_NAME = "_AUTO_CELL"; /** * The user defined label for the the contact method. * <P>Type: TEXT</P> Loading Loading @@ -1005,9 +1016,15 @@ public class Contacts { } } else { if (!TextUtils.isEmpty(label)) { if (label.toString().equals(MOBILE_EMAIL_TYPE_NAME)) { display = context.getString( com.android.internal.R.string.mobileEmailTypeName); } else { display = label; } } } break; } Loading
core/java/android/syncml/pim/PropertyNode.java +180 −14 Original line number Diff line number Diff line Loading @@ -17,12 +17,16 @@ package android.syncml.pim; import android.content.ContentValues; import android.util.Log; import org.apache.commons.codec.binary.Base64; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Map.Entry; import java.util.regex.Pattern; public class PropertyNode { Loading Loading @@ -52,7 +56,9 @@ public class PropertyNode { public Set<String> propGroupSet; public PropertyNode() { propName = ""; propValue = ""; propValue_vector = new ArrayList<String>(); paramMap = new ContentValues(); paramMap_TYPE = new HashSet<String>(); propGroupSet = new HashSet<String>(); Loading @@ -62,13 +68,21 @@ public class PropertyNode { String propName, String propValue, List<String> propValue_vector, byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE, Set<String> propGroupSet) { if (propName != null) { this.propName = propName; } else { this.propName = ""; } if (propValue != null) { this.propValue = propValue; } else { this.propValue = ""; } if (propValue_vector != null) { this.propValue_vector = propValue_vector; } else { this.propValue_vector = new ArrayList<String>(); } this.propValue_bytes = propValue_bytes; if (paramMap != null) { this.paramMap = paramMap; Loading Loading @@ -117,17 +131,9 @@ public class PropertyNode { // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector // is 1, the encoded value is stored in propValue, so we do not have to // check it. if (propValue_vector != null) { // Log.d("@@@", "===" + propValue_vector + ", " + node.propValue_vector); return (propValue_vector.equals(node.propValue_vector) || (propValue_vector.size() == 1)); } else if (node.propValue_vector != null) { // Log.d("@@@", "===" + propValue_vector + ", " + node.propValue_vector); return (node.propValue_vector.equals(propValue_vector) || (node.propValue_vector.size() == 1)); } else { return true; } propValue_vector.size() == 1 || node.propValue_vector.size() == 1); } } Loading @@ -154,4 +160,164 @@ public class PropertyNode { builder.append(propValue); 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; } }
core/java/android/syncml/pim/VBuilderCollection.java 0 → 100644 +100 −0 Original line number 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 android.syncml.pim; import java.util.Collection; import java.util.List; public class VBuilderCollection implements VBuilder { private final Collection<VBuilder> mVBuilderCollection; public VBuilderCollection(Collection<VBuilder> vBuilderCollection) { mVBuilderCollection = vBuilderCollection; } public Collection<VBuilder> getVBuilderCollection() { return mVBuilderCollection; } public void start() { for (VBuilder builder : mVBuilderCollection) { builder.start(); } } public void end() { for (VBuilder builder : mVBuilderCollection) { builder.end(); } } public void startRecord(String type) { for (VBuilder builder : mVBuilderCollection) { builder.startRecord(type); } } public void endRecord() { for (VBuilder builder : mVBuilderCollection) { builder.endRecord(); } } public void startProperty() { for (VBuilder builder : mVBuilderCollection) { builder.startProperty(); } } public void endProperty() { for (VBuilder builder : mVBuilderCollection) { builder.endProperty(); } } public void propertyGroup(String group) { for (VBuilder builder : mVBuilderCollection) { builder.propertyGroup(group); } } public void propertyName(String name) { for (VBuilder builder : mVBuilderCollection) { builder.propertyName(name); } } public void propertyParamType(String type) { for (VBuilder builder : mVBuilderCollection) { builder.propertyParamType(type); } } public void propertyParamValue(String value) { for (VBuilder builder : mVBuilderCollection) { builder.propertyParamValue(value); } } public void propertyValues(List<String> values) { for (VBuilder builder : mVBuilderCollection) { builder.propertyValues(values); } } }