am 255d51cc: Merge change Ia6fc04c8 into eclair
Merge commit '255d51ccac792305253dc5953e136dae65e74266' into eclair-plus-aosp * commit '255d51ccac792305253dc5953e136dae65e74266': Remove android.syncml package completely.
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
<HTML>
|
||||
<BODY>
|
||||
Support classes for SyncML.
|
||||
{@hide}
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -1,324 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.content.ContentValues;
|
||||
|
||||
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;
|
||||
|
||||
@Deprecated
|
||||
public class PropertyNode {
|
||||
|
||||
public String propName;
|
||||
|
||||
public String propValue;
|
||||
|
||||
public List<String> propValue_vector;
|
||||
|
||||
/** Store value as byte[],after decode.
|
||||
* Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc.
|
||||
*/
|
||||
public byte[] propValue_bytes;
|
||||
|
||||
/** param store: key=paramType, value=paramValue
|
||||
* Note that currently PropertyNode class does not support multiple param-values
|
||||
* defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as
|
||||
* one String value like "A,B", not ["A", "B"]...
|
||||
* TODO: fix this.
|
||||
*/
|
||||
public ContentValues paramMap;
|
||||
|
||||
/** Only for TYPE=??? param store. */
|
||||
public Set<String> paramMap_TYPE;
|
||||
|
||||
/** Store group values. Used only in VCard. */
|
||||
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>();
|
||||
}
|
||||
|
||||
public 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;
|
||||
} else {
|
||||
this.paramMap = new ContentValues();
|
||||
}
|
||||
if (paramMap_TYPE != null) {
|
||||
this.paramMap_TYPE = paramMap_TYPE;
|
||||
} else {
|
||||
this.paramMap_TYPE = new HashSet<String>();
|
||||
}
|
||||
if (propGroupSet != null) {
|
||||
this.propGroupSet = propGroupSet;
|
||||
} else {
|
||||
this.propGroupSet = new HashSet<String>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof PropertyNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertyNode node = (PropertyNode)obj;
|
||||
|
||||
if (propName == null || !propName.equals(node.propName)) {
|
||||
return false;
|
||||
} else if (!paramMap.equals(node.paramMap)) {
|
||||
return false;
|
||||
} else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
|
||||
return false;
|
||||
} else if (!propGroupSet.equals(node.propGroupSet)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) {
|
||||
return true;
|
||||
} else {
|
||||
// Log.d("@@@", propValue + ", " + node.propValue);
|
||||
if (!propValue.equals(node.propValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The value in propValue_vector is not decoded even if it should be
|
||||
// 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.
|
||||
return (propValue_vector.equals(node.propValue_vector) ||
|
||||
propValue_vector.size() == 1 ||
|
||||
node.propValue_vector.size() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("propName: ");
|
||||
builder.append(propName);
|
||||
builder.append(", paramMap: ");
|
||||
builder.append(paramMap.toString());
|
||||
builder.append(", propmMap_TYPE: ");
|
||||
builder.append(paramMap_TYPE.toString());
|
||||
builder.append(", propGroupSet: ");
|
||||
builder.append(propGroupSet.toString());
|
||||
if (propValue_vector != null && propValue_vector.size() > 1) {
|
||||
builder.append(", propValue_vector size: ");
|
||||
builder.append(propValue_vector.size());
|
||||
}
|
||||
if (propValue_bytes != null) {
|
||||
builder.append(", propValue_bytes size: ");
|
||||
builder.append(propValue_bytes.length);
|
||||
}
|
||||
builder.append(", propValue: ");
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.List;
|
||||
|
||||
@Deprecated
|
||||
public interface VBuilder {
|
||||
void start();
|
||||
|
||||
void end();
|
||||
|
||||
/**
|
||||
* @param type
|
||||
* VXX <br>
|
||||
* BEGIN:VXX
|
||||
*/
|
||||
void startRecord(String type);
|
||||
|
||||
/** END:VXX */
|
||||
void endRecord();
|
||||
|
||||
void startProperty();
|
||||
|
||||
void endProperty();
|
||||
|
||||
/**
|
||||
* @param group
|
||||
*/
|
||||
void propertyGroup(String group);
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* N <br>
|
||||
* N
|
||||
*/
|
||||
void propertyName(String name);
|
||||
|
||||
/**
|
||||
* @param type
|
||||
* LANGUAGE \ ENCODING <br>
|
||||
* ;LANGUage= \ ;ENCODING=
|
||||
*/
|
||||
void propertyParamType(String type);
|
||||
|
||||
/**
|
||||
* @param value
|
||||
* FR-EN \ GBK <br>
|
||||
* FR-EN \ GBK
|
||||
*/
|
||||
void propertyParamValue(String value);
|
||||
|
||||
void propertyValues(List<String> values);
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
@Deprecated
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,312 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.content.ContentValues;
|
||||
import android.util.CharsetUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.net.QuotedPrintableCodec;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Store the parse result to custom datastruct: VNode, PropertyNode
|
||||
* Maybe several vcard instance, so use vNodeList to store.
|
||||
* VNode: standy by a vcard instance.
|
||||
* PropertyNode: standy by a property line of a card.
|
||||
*/
|
||||
@Deprecated
|
||||
public class VDataBuilder implements VBuilder {
|
||||
static private String LOG_TAG = "VDATABuilder";
|
||||
|
||||
/**
|
||||
* If there's no other information available, this class uses this charset for encoding
|
||||
* byte arrays.
|
||||
*/
|
||||
static public String DEFAULT_CHARSET = "UTF-8";
|
||||
|
||||
/** type=VNode */
|
||||
public List<VNode> vNodeList = new ArrayList<VNode>();
|
||||
private int mNodeListPos = 0;
|
||||
private VNode mCurrentVNode;
|
||||
private PropertyNode mCurrentPropNode;
|
||||
private String mCurrentParamType;
|
||||
|
||||
/**
|
||||
* The charset using which VParser parses the text.
|
||||
*/
|
||||
private String mSourceCharset;
|
||||
|
||||
/**
|
||||
* The charset with which byte array is encoded to String.
|
||||
*/
|
||||
private String mTargetCharset;
|
||||
|
||||
private boolean mStrictLineBreakParsing;
|
||||
|
||||
public VDataBuilder() {
|
||||
this(VParser.DEFAULT_CHARSET, DEFAULT_CHARSET, false);
|
||||
}
|
||||
|
||||
public VDataBuilder(String charset, boolean strictLineBreakParsing) {
|
||||
this(null, charset, strictLineBreakParsing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide sourceCharset is temporal.
|
||||
*/
|
||||
public VDataBuilder(String sourceCharset, String targetCharset,
|
||||
boolean strictLineBreakParsing) {
|
||||
if (sourceCharset != null) {
|
||||
mSourceCharset = sourceCharset;
|
||||
} else {
|
||||
mSourceCharset = VParser.DEFAULT_CHARSET;
|
||||
}
|
||||
if (targetCharset != null) {
|
||||
mTargetCharset = targetCharset;
|
||||
} else {
|
||||
mTargetCharset = DEFAULT_CHARSET;
|
||||
}
|
||||
mStrictLineBreakParsing = strictLineBreakParsing;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
}
|
||||
|
||||
public void end() {
|
||||
}
|
||||
|
||||
// Note: I guess that this code assumes the Record may nest like this:
|
||||
// START:VPOS
|
||||
// ...
|
||||
// START:VPOS2
|
||||
// ...
|
||||
// END:VPOS2
|
||||
// ...
|
||||
// END:VPOS
|
||||
//
|
||||
// However the following code has a bug.
|
||||
// When error occurs after calling startRecord(), the entry which is probably
|
||||
// the cause of the error remains to be in vNodeList, while endRecord() is not called.
|
||||
//
|
||||
// I leave this code as is since I'm not familiar with vcalendar specification.
|
||||
// But I believe we should refactor this code in the future.
|
||||
// Until this, the last entry has to be removed when some error occurs.
|
||||
public void startRecord(String type) {
|
||||
|
||||
VNode vnode = new VNode();
|
||||
vnode.parseStatus = 1;
|
||||
vnode.VName = type;
|
||||
// I feel this should be done in endRecord(), but it cannot be done because of
|
||||
// the reason above.
|
||||
vNodeList.add(vnode);
|
||||
mNodeListPos = vNodeList.size() - 1;
|
||||
mCurrentVNode = vNodeList.get(mNodeListPos);
|
||||
}
|
||||
|
||||
public void endRecord() {
|
||||
VNode endNode = vNodeList.get(mNodeListPos);
|
||||
endNode.parseStatus = 0;
|
||||
while(mNodeListPos > 0){
|
||||
mNodeListPos--;
|
||||
if((vNodeList.get(mNodeListPos)).parseStatus == 1)
|
||||
break;
|
||||
}
|
||||
mCurrentVNode = vNodeList.get(mNodeListPos);
|
||||
}
|
||||
|
||||
public void startProperty() {
|
||||
mCurrentPropNode = new PropertyNode();
|
||||
}
|
||||
|
||||
public void endProperty() {
|
||||
mCurrentVNode.propList.add(mCurrentPropNode);
|
||||
}
|
||||
|
||||
public void propertyName(String name) {
|
||||
mCurrentPropNode.propName = name;
|
||||
}
|
||||
|
||||
// Used only in VCard.
|
||||
public void propertyGroup(String group) {
|
||||
mCurrentPropNode.propGroupSet.add(group);
|
||||
}
|
||||
|
||||
public void propertyParamType(String type) {
|
||||
mCurrentParamType = type;
|
||||
}
|
||||
|
||||
public void propertyParamValue(String value) {
|
||||
if (mCurrentParamType == null ||
|
||||
mCurrentParamType.equalsIgnoreCase("TYPE")) {
|
||||
mCurrentPropNode.paramMap_TYPE.add(value);
|
||||
} else {
|
||||
mCurrentPropNode.paramMap.put(mCurrentParamType, value);
|
||||
}
|
||||
|
||||
mCurrentParamType = null;
|
||||
}
|
||||
|
||||
private String encodeString(String originalString, String targetCharset) {
|
||||
if (mSourceCharset.equalsIgnoreCase(targetCharset)) {
|
||||
return originalString;
|
||||
}
|
||||
Charset charset = Charset.forName(mSourceCharset);
|
||||
ByteBuffer byteBuffer = charset.encode(originalString);
|
||||
// byteBuffer.array() "may" return byte array which is larger than
|
||||
// byteBuffer.remaining(). Here, we keep on the safe side.
|
||||
byte[] bytes = new byte[byteBuffer.remaining()];
|
||||
byteBuffer.get(bytes);
|
||||
try {
|
||||
return new String(bytes, targetCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
|
||||
return new String(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
private String handleOneValue(String value, String targetCharset, String encoding) {
|
||||
if (encoding != null) {
|
||||
if (encoding.equals("BASE64") || encoding.equals("B")) {
|
||||
// Assume BASE64 is used only when the number of values is 1.
|
||||
mCurrentPropNode.propValue_bytes =
|
||||
Base64.decodeBase64(value.getBytes());
|
||||
return value;
|
||||
} else if (encoding.equals("QUOTED-PRINTABLE")) {
|
||||
String quotedPrintable = value
|
||||
.replaceAll("= ", " ").replaceAll("=\t", "\t");
|
||||
String[] lines;
|
||||
if (mStrictLineBreakParsing) {
|
||||
lines = quotedPrintable.split("\r\n");
|
||||
} else {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int length = quotedPrintable.length();
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = quotedPrintable.charAt(i);
|
||||
if (ch == '\n') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
} else if (ch == '\r') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
if (i < length - 1) {
|
||||
char nextCh = quotedPrintable.charAt(i + 1);
|
||||
if (nextCh == '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
String finalLine = builder.toString();
|
||||
if (finalLine.length() > 0) {
|
||||
list.add(finalLine);
|
||||
}
|
||||
lines = list.toArray(new String[0]);
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String line : lines) {
|
||||
if (line.endsWith("=")) {
|
||||
line = line.substring(0, line.length() - 1);
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = builder.toString().getBytes(mSourceCharset);
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset);
|
||||
bytes = builder.toString().getBytes();
|
||||
}
|
||||
|
||||
try {
|
||||
bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
|
||||
} catch (DecoderException e) {
|
||||
Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
return new String(bytes, targetCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
|
||||
return new String(bytes);
|
||||
}
|
||||
}
|
||||
// Unknown encoding. Fall back to default.
|
||||
}
|
||||
return encodeString(value, targetCharset);
|
||||
}
|
||||
|
||||
public void propertyValues(List<String> values) {
|
||||
if (values == null || values.size() == 0) {
|
||||
mCurrentPropNode.propValue_bytes = null;
|
||||
mCurrentPropNode.propValue_vector.clear();
|
||||
mCurrentPropNode.propValue_vector.add("");
|
||||
mCurrentPropNode.propValue = "";
|
||||
return;
|
||||
}
|
||||
|
||||
ContentValues paramMap = mCurrentPropNode.paramMap;
|
||||
|
||||
String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET"));
|
||||
String encoding = paramMap.getAsString("ENCODING");
|
||||
|
||||
if (targetCharset == null || targetCharset.length() == 0) {
|
||||
targetCharset = mTargetCharset;
|
||||
}
|
||||
|
||||
for (String value : values) {
|
||||
mCurrentPropNode.propValue_vector.add(
|
||||
handleOneValue(value, targetCharset, encoding));
|
||||
}
|
||||
|
||||
mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector);
|
||||
}
|
||||
|
||||
private String listToString(List<String> list){
|
||||
int size = list.size();
|
||||
if (size > 1) {
|
||||
StringBuilder typeListB = new StringBuilder();
|
||||
for (String type : list) {
|
||||
typeListB.append(type).append(";");
|
||||
}
|
||||
int len = typeListB.length();
|
||||
if (len > 0 && typeListB.charAt(len - 1) == ';') {
|
||||
return typeListB.substring(0, len - 1);
|
||||
}
|
||||
return typeListB.toString();
|
||||
} else if (size == 1) {
|
||||
return list.get(0);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public String getResult(){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.ArrayList;
|
||||
|
||||
@Deprecated
|
||||
public class VNode {
|
||||
|
||||
public String VName;
|
||||
|
||||
public ArrayList<PropertyNode> propList = new ArrayList<PropertyNode>();
|
||||
|
||||
/** 0:parse over. 1:parsing. */
|
||||
public int parseStatus = 1;
|
||||
}
|
||||
@@ -1,740 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* This interface is used to parse the V format files, such as VCard & VCal
|
||||
*/
|
||||
@Deprecated
|
||||
abstract public class VParser {
|
||||
// Assume that "iso-8859-1" is able to map "all" 8bit characters to some unicode and
|
||||
// decode the unicode to the original charset. If not, this setting will cause some bug.
|
||||
public static String DEFAULT_CHARSET = "iso-8859-1";
|
||||
|
||||
/**
|
||||
* The buffer used to store input stream
|
||||
*/
|
||||
protected String mBuffer = null;
|
||||
|
||||
/** The builder to build parsed data */
|
||||
protected VBuilder mBuilder = null;
|
||||
|
||||
/** The encoding type */
|
||||
protected String mEncoding = null;
|
||||
|
||||
protected final int PARSE_ERROR = -1;
|
||||
|
||||
protected final String mDefaultEncoding = "8BIT";
|
||||
|
||||
/**
|
||||
* If offset reach '\r\n' return 2. Else return PARSE_ERROR.
|
||||
*/
|
||||
protected int parseCrlf(int offset) {
|
||||
if (offset >= mBuffer.length())
|
||||
return PARSE_ERROR;
|
||||
char ch = mBuffer.charAt(offset);
|
||||
if (ch == '\r') {
|
||||
offset++;
|
||||
ch = mBuffer.charAt(offset);
|
||||
if (ch == '\n') {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given stream
|
||||
*
|
||||
* @param is
|
||||
* The source to parse.
|
||||
* @param encoding
|
||||
* The encoding type.
|
||||
* @param builder
|
||||
* The v builder which used to construct data.
|
||||
* @return Return true for success, otherwise false.
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean parse(InputStream is, String encoding, VBuilder builder)
|
||||
throws IOException {
|
||||
setInputStream(is, encoding);
|
||||
mBuilder = builder;
|
||||
int ret = 0, offset = 0, sum = 0;
|
||||
|
||||
if (mBuilder != null) {
|
||||
mBuilder.start();
|
||||
}
|
||||
for (;;) {
|
||||
ret = parseVFile(offset); // for next property length
|
||||
if (PARSE_ERROR == ret) {
|
||||
break;
|
||||
} else {
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
}
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.end();
|
||||
}
|
||||
return (mBuffer.length() == sum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given stream with the default encoding.
|
||||
*
|
||||
* @param is
|
||||
* The source to parse.
|
||||
* @param builder
|
||||
* The v builder which used to construct data.
|
||||
* @return Return true for success, otherwise false.
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean parse(InputStream is, VBuilder builder) throws IOException {
|
||||
return parse(is, DEFAULT_CHARSET, builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the content of input stream and filter the "folding"
|
||||
*/
|
||||
protected void setInputStream(InputStream is, String encoding)
|
||||
throws UnsupportedEncodingException {
|
||||
InputStreamReader reader = new InputStreamReader(is, encoding);
|
||||
StringBuilder b = new StringBuilder();
|
||||
|
||||
int ch;
|
||||
try {
|
||||
while ((ch = reader.read()) != -1) {
|
||||
if (ch == '\r') {
|
||||
ch = reader.read();
|
||||
if (ch == '\n') {
|
||||
ch = reader.read();
|
||||
if (ch == ' ' || ch == '\t') {
|
||||
b.append((char) ch);
|
||||
continue;
|
||||
}
|
||||
b.append("\r\n");
|
||||
if (ch == -1) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
b.append("\r");
|
||||
}
|
||||
}
|
||||
b.append((char) ch);
|
||||
}
|
||||
mBuffer = b.toString();
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* abstract function, waiting implement.<br>
|
||||
* analyse from offset, return the length of consumed property.
|
||||
*/
|
||||
abstract protected int parseVFile(int offset);
|
||||
|
||||
/**
|
||||
* From offset, jump ' ', '\t', '\r\n' sequence, return the length of jump.<br>
|
||||
* 1 * (SPACE / HTAB / CRLF)
|
||||
*/
|
||||
protected int parseWsls(int offset) {
|
||||
int ret = 0, sum = 0;
|
||||
|
||||
try {
|
||||
char ch = mBuffer.charAt(offset);
|
||||
if (ch == ' ' || ch == '\t') {
|
||||
sum++;
|
||||
offset++;
|
||||
} else if ((ret = parseCrlf(offset)) != PARSE_ERROR) {
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
} else {
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
for (;;) {
|
||||
ch = mBuffer.charAt(offset);
|
||||
if (ch == ' ' || ch == '\t') {
|
||||
sum++;
|
||||
offset++;
|
||||
} else if ((ret = parseCrlf(offset)) != PARSE_ERROR) {
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
;
|
||||
}
|
||||
if (sum > 0)
|
||||
return sum;
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* To determine if the given string equals to the start of the current
|
||||
* string.
|
||||
*
|
||||
* @param offset
|
||||
* The offset in buffer of current string
|
||||
* @param tar
|
||||
* The given string.
|
||||
* @param ignoreCase
|
||||
* To determine case sensitive or not.
|
||||
* @return The consumed characters, otherwise return PARSE_ERROR.
|
||||
*/
|
||||
protected int parseString(int offset, final String tar, boolean ignoreCase) {
|
||||
int sum = 0;
|
||||
if (tar == null) {
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
if (ignoreCase) {
|
||||
int len = tar.length();
|
||||
try {
|
||||
if (mBuffer.substring(offset, offset + len).equalsIgnoreCase(
|
||||
tar)) {
|
||||
sum = len;
|
||||
} else {
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
} else { /* case sensitive */
|
||||
if (mBuffer.startsWith(tar, offset)) {
|
||||
sum = tar.length();
|
||||
} else {
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the white space in string.
|
||||
*/
|
||||
protected int removeWs(int offset) {
|
||||
if (offset >= mBuffer.length())
|
||||
return PARSE_ERROR;
|
||||
int sum = 0;
|
||||
char ch;
|
||||
while ((ch = mBuffer.charAt(offset)) == ' ' || ch == '\t') {
|
||||
offset++;
|
||||
sum++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* "X-" word, and its value. Return consumed length.
|
||||
*/
|
||||
protected int parseXWord(int offset) {
|
||||
int ret = 0, sum = 0;
|
||||
ret = parseString(offset, "X-", true);
|
||||
if (PARSE_ERROR == ret)
|
||||
return PARSE_ERROR;
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
|
||||
ret = parseWord(offset);
|
||||
if (PARSE_ERROR == ret) {
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* From offset, parse as :mEncoding ?= 7bit / 8bit / quoted-printable /
|
||||
* base64
|
||||
*/
|
||||
protected int parseValue(int offset) {
|
||||
int ret = 0;
|
||||
|
||||
if (mEncoding == null || mEncoding.equalsIgnoreCase("7BIT")
|
||||
|| mEncoding.equalsIgnoreCase("8BIT")
|
||||
|| mEncoding.toUpperCase().startsWith("X-")) {
|
||||
ret = parse8bit(offset);
|
||||
if (ret != PARSE_ERROR) {
|
||||
return ret;
|
||||
}
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
|
||||
ret = parseQuotedPrintable(offset);
|
||||
if (ret != PARSE_ERROR) {
|
||||
return ret;
|
||||
}
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
if (mEncoding.equalsIgnoreCase("BASE64")) {
|
||||
ret = parseBase64(offset);
|
||||
if (ret != PARSE_ERROR) {
|
||||
return ret;
|
||||
}
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refer to RFC 1521, 8bit text
|
||||
*/
|
||||
protected int parse8bit(int offset) {
|
||||
int index = 0;
|
||||
|
||||
index = mBuffer.substring(offset).indexOf("\r\n");
|
||||
|
||||
if (index == -1)
|
||||
return PARSE_ERROR;
|
||||
else
|
||||
return index;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Refer to RFC 1521, quoted printable text ([*(ptext / SPACE / TAB) ptext]
|
||||
* ["="] CRLF)
|
||||
*/
|
||||
protected int parseQuotedPrintable(int offset) {
|
||||
int ret = 0, sum = 0;
|
||||
|
||||
ret = removeWs(offset);
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
|
||||
for (;;) {
|
||||
ret = parsePtext(offset);
|
||||
if (PARSE_ERROR == ret)
|
||||
break;
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
|
||||
ret = removeWs(offset);
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "=", false);
|
||||
if (ret != PARSE_ERROR) {
|
||||
// offset += ret;
|
||||
sum += ret;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* return 1 or 3 <any ASCII character except "=", SPACE, or TAB>
|
||||
*/
|
||||
protected int parsePtext(int offset) {
|
||||
int ret = 0;
|
||||
|
||||
try {
|
||||
char ch = mBuffer.charAt(offset);
|
||||
if (isPrintable(ch) && ch != '=' && ch != ' ' && ch != '\t') {
|
||||
return 1;
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
ret = parseOctet(offset);
|
||||
if (ret != PARSE_ERROR) {
|
||||
return ret;
|
||||
}
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* start with "=" two of (DIGIT / "A" / "B" / "C" / "D" / "E" / "F") <br>
|
||||
* So maybe return 3.
|
||||
*/
|
||||
protected int parseOctet(int offset) {
|
||||
int ret = 0, sum = 0;
|
||||
|
||||
ret = parseString(offset, "=", false);
|
||||
if (PARSE_ERROR == ret)
|
||||
return PARSE_ERROR;
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
|
||||
try {
|
||||
int ch = mBuffer.charAt(offset);
|
||||
if (ch == ' ' || ch == '\t')
|
||||
return ++sum;
|
||||
if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
|
||||
offset++;
|
||||
sum++;
|
||||
ch = mBuffer.charAt(offset);
|
||||
if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
|
||||
sum++;
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
;
|
||||
}
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refer to RFC 1521, base64 text The end of the text is marked with two
|
||||
* CRLF sequences
|
||||
*/
|
||||
protected int parseBase64(int offset) {
|
||||
int sum = 0;
|
||||
try {
|
||||
for (;;) {
|
||||
char ch;
|
||||
ch = mBuffer.charAt(offset);
|
||||
|
||||
if (ch == '\r') {
|
||||
int ret = parseString(offset, "\r\n\r\n", false);
|
||||
sum += ret;
|
||||
break;
|
||||
} else {
|
||||
/* ignore none base64 character */
|
||||
sum++;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
sum -= 2;/* leave one CRLF to parse the end of this property */
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Any printable ASCII sequence except [ ]=:.,;
|
||||
*/
|
||||
protected int parseWord(int offset) {
|
||||
int sum = 0;
|
||||
try {
|
||||
for (;;) {
|
||||
char ch = mBuffer.charAt(offset);
|
||||
if (!isPrintable(ch))
|
||||
break;
|
||||
if (ch == ' ' || ch == '=' || ch == ':' || ch == '.'
|
||||
|| ch == ',' || ch == ';')
|
||||
break;
|
||||
if (ch == '\\') {
|
||||
ch = mBuffer.charAt(offset + 1);
|
||||
if (ch == ';') {
|
||||
offset++;
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
offset++;
|
||||
sum++;
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
;
|
||||
}
|
||||
if (sum == 0)
|
||||
return PARSE_ERROR;
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* If it is a letter or digit.
|
||||
*/
|
||||
protected boolean isLetterOrDigit(char ch) {
|
||||
if (ch >= '0' && ch <= '9')
|
||||
return true;
|
||||
if (ch >= 'a' && ch <= 'z')
|
||||
return true;
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If it is printable in ASCII
|
||||
*/
|
||||
protected boolean isPrintable(char ch) {
|
||||
if (ch >= ' ' && ch <= '~')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If it is a letter.
|
||||
*/
|
||||
protected boolean isLetter(char ch) {
|
||||
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a word from current position.
|
||||
*/
|
||||
protected String getWord(int offset) {
|
||||
StringBuilder word = new StringBuilder();
|
||||
try {
|
||||
for (;;) {
|
||||
char ch = mBuffer.charAt(offset);
|
||||
if (isLetterOrDigit(ch) || ch == '-') {
|
||||
word.append(ch);
|
||||
offset++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
;
|
||||
}
|
||||
return word.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* If is: "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
|
||||
*/
|
||||
protected int parsePValueVal(int offset) {
|
||||
int ret = 0, sum = 0;
|
||||
|
||||
ret = parseString(offset, "INLINE", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "URL", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "CONTENT-ID", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "CID", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "INLINE", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseXWord(offset);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* If is: "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word and
|
||||
* set mEncoding.
|
||||
*/
|
||||
protected int parsePEncodingVal(int offset) {
|
||||
int ret = 0, sum = 0;
|
||||
|
||||
ret = parseString(offset, "7BIT", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
mEncoding = "7BIT";
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "8BIT", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
mEncoding = "8BIT";
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "QUOTED-PRINTABLE", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
mEncoding = "QUOTED-PRINTABLE";
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "BASE64", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
mEncoding = "BASE64";
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseXWord(offset);
|
||||
if (ret != PARSE_ERROR) {
|
||||
mEncoding = mBuffer.substring(offset).substring(0, ret);
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refer to RFC1521, section 7.1<br>
|
||||
* If is: "us-ascii" / "iso-8859-xxx" / "X-" word
|
||||
*/
|
||||
protected int parseCharsetVal(int offset) {
|
||||
int ret = 0, sum = 0;
|
||||
|
||||
ret = parseString(offset, "us-ascii", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "iso-8859-1", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "iso-8859-2", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "iso-8859-3", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "iso-8859-4", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "iso-8859-5", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "iso-8859-6", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "iso-8859-7", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "iso-8859-8", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseString(offset, "iso-8859-9", true);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
ret = parseXWord(offset);
|
||||
if (ret != PARSE_ERROR) {
|
||||
sum += ret;
|
||||
return sum;
|
||||
}
|
||||
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refer to RFC 1766<br>
|
||||
* like: XXX(sequence letters)-XXX(sequence letters)
|
||||
*/
|
||||
protected int parseLangVal(int offset) {
|
||||
int ret = 0, sum = 0;
|
||||
|
||||
ret = parseTag(offset);
|
||||
if (PARSE_ERROR == ret) {
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
|
||||
for (;;) {
|
||||
ret = parseString(offset, "-", false);
|
||||
if (PARSE_ERROR == ret) {
|
||||
break;
|
||||
}
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
|
||||
ret = parseTag(offset);
|
||||
if (PARSE_ERROR == ret) {
|
||||
break;
|
||||
}
|
||||
offset += ret;
|
||||
sum += ret;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* From first 8 position, is sequence LETTER.
|
||||
*/
|
||||
protected int parseTag(int offset) {
|
||||
int sum = 0, i = 0;
|
||||
|
||||
try {
|
||||
for (i = 0; i < 8; i++) {
|
||||
char ch = mBuffer.charAt(offset);
|
||||
if (!isLetter(ch)) {
|
||||
break;
|
||||
}
|
||||
sum++;
|
||||
offset++;
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
;
|
||||
}
|
||||
if (i == 0) {
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<HTML>
|
||||
<BODY>
|
||||
Support classes for SyncML.
|
||||
{@hide}
|
||||
</BODY>
|
||||
</HTML>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,358 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.vcard;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import android.provider.Contacts;
|
||||
import android.syncml.pim.vcard.ContactStruct.PhoneData;
|
||||
|
||||
/**
|
||||
* Compose VCard string
|
||||
*/
|
||||
@Deprecated
|
||||
public class VCardComposer {
|
||||
final public static int VERSION_VCARD21_INT = 1;
|
||||
|
||||
final public static int VERSION_VCARD30_INT = 2;
|
||||
|
||||
/**
|
||||
* A new line
|
||||
*/
|
||||
private String mNewline;
|
||||
|
||||
/**
|
||||
* The composed string
|
||||
*/
|
||||
private StringBuilder mResult;
|
||||
|
||||
/**
|
||||
* The email's type
|
||||
*/
|
||||
static final private HashSet<String> emailTypes = new HashSet<String>(
|
||||
Arrays.asList("CELL", "AOL", "APPLELINK", "ATTMAIL", "CIS",
|
||||
"EWORLD", "INTERNET", "IBMMAIL", "MCIMAIL", "POWERSHARE",
|
||||
"PRODIGY", "TLX", "X400"));
|
||||
|
||||
static final private HashSet<String> phoneTypes = new HashSet<String>(
|
||||
Arrays.asList("PREF", "WORK", "HOME", "VOICE", "FAX", "MSG",
|
||||
"CELL", "PAGER", "BBS", "MODEM", "CAR", "ISDN", "VIDEO"));
|
||||
|
||||
static final private String TAG = "VCardComposer";
|
||||
|
||||
public VCardComposer() {
|
||||
}
|
||||
|
||||
private static final HashMap<Integer, String> phoneTypeMap = new HashMap<Integer, String>();
|
||||
|
||||
private static final HashMap<Integer, String> emailTypeMap = new HashMap<Integer, String>();
|
||||
|
||||
static {
|
||||
phoneTypeMap.put(Contacts.Phones.TYPE_HOME, "HOME");
|
||||
phoneTypeMap.put(Contacts.Phones.TYPE_MOBILE, "CELL");
|
||||
phoneTypeMap.put(Contacts.Phones.TYPE_WORK, "WORK");
|
||||
// FAX_WORK not exist in vcard spec. The approximate is the combine of
|
||||
// WORK and FAX, here only map to FAX
|
||||
phoneTypeMap.put(Contacts.Phones.TYPE_FAX_WORK, "WORK;FAX");
|
||||
phoneTypeMap.put(Contacts.Phones.TYPE_FAX_HOME, "HOME;FAX");
|
||||
phoneTypeMap.put(Contacts.Phones.TYPE_PAGER, "PAGER");
|
||||
phoneTypeMap.put(Contacts.Phones.TYPE_OTHER, "X-OTHER");
|
||||
emailTypeMap.put(Contacts.ContactMethods.TYPE_HOME, "HOME");
|
||||
emailTypeMap.put(Contacts.ContactMethods.TYPE_WORK, "WORK");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a vCard String.
|
||||
*
|
||||
* @param struct
|
||||
* see more from ContactStruct class
|
||||
* @param vcardversion
|
||||
* MUST be VERSION_VCARD21 /VERSION_VCARD30
|
||||
* @return vCard string
|
||||
* @throws VCardException
|
||||
* struct.name is null /vcardversion not match
|
||||
*/
|
||||
public String createVCard(ContactStruct struct, int vcardversion)
|
||||
throws VCardException {
|
||||
|
||||
mResult = new StringBuilder();
|
||||
// check exception:
|
||||
if (struct.name == null || struct.name.trim().equals("")) {
|
||||
throw new VCardException(" struct.name MUST have value.");
|
||||
}
|
||||
if (vcardversion == VERSION_VCARD21_INT) {
|
||||
mNewline = "\r\n";
|
||||
} else if (vcardversion == VERSION_VCARD30_INT) {
|
||||
mNewline = "\n";
|
||||
} else {
|
||||
throw new VCardException(
|
||||
" version not match VERSION_VCARD21 or VERSION_VCARD30.");
|
||||
}
|
||||
// build vcard:
|
||||
mResult.append("BEGIN:VCARD").append(mNewline);
|
||||
|
||||
if (vcardversion == VERSION_VCARD21_INT) {
|
||||
mResult.append("VERSION:2.1").append(mNewline);
|
||||
} else {
|
||||
mResult.append("VERSION:3.0").append(mNewline);
|
||||
}
|
||||
|
||||
if (!isNull(struct.name)) {
|
||||
appendNameStr(struct.name);
|
||||
}
|
||||
|
||||
if (!isNull(struct.company)) {
|
||||
mResult.append("ORG:").append(struct.company).append(mNewline);
|
||||
}
|
||||
|
||||
if (struct.notes.size() > 0 && !isNull(struct.notes.get(0))) {
|
||||
mResult.append("NOTE:").append(
|
||||
foldingString(struct.notes.get(0), vcardversion)).append(mNewline);
|
||||
}
|
||||
|
||||
if (!isNull(struct.title)) {
|
||||
mResult.append("TITLE:").append(
|
||||
foldingString(struct.title, vcardversion)).append(mNewline);
|
||||
}
|
||||
|
||||
if (struct.photoBytes != null) {
|
||||
appendPhotoStr(struct.photoBytes, struct.photoType, vcardversion);
|
||||
}
|
||||
|
||||
if (struct.phoneList != null) {
|
||||
appendPhoneStr(struct.phoneList, vcardversion);
|
||||
}
|
||||
|
||||
if (struct.contactmethodList != null) {
|
||||
appendContactMethodStr(struct.contactmethodList, vcardversion);
|
||||
}
|
||||
|
||||
if (!isNull(struct.timeStamp)) {
|
||||
mResult.append(struct.timeStamp).append(mNewline);
|
||||
}
|
||||
|
||||
mResult.append("END:VCARD").append(mNewline);
|
||||
return mResult.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter str to folding supported format.
|
||||
*
|
||||
* @param str
|
||||
* the string to be folded
|
||||
* @param version
|
||||
* the vcard version
|
||||
* @return the folded string
|
||||
*/
|
||||
private String foldingString(String str, int version) {
|
||||
if (str.endsWith("\r\n")) {
|
||||
str = str.substring(0, str.length() - 2);
|
||||
} else if (str.endsWith("\n")) {
|
||||
str = str.substring(0, str.length() - 1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
str = str.replaceAll("\r\n", "\n");
|
||||
if (version == VERSION_VCARD21_INT) {
|
||||
return str.replaceAll("\n", "\r\n ");
|
||||
} else if (version == VERSION_VCARD30_INT) {
|
||||
return str.replaceAll("\n", "\n ");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LOGO property. format LOGO's param and encode value as base64.
|
||||
*
|
||||
* @param bytes
|
||||
* the binary string to be converted
|
||||
* @param type
|
||||
* the type of the content
|
||||
* @param version
|
||||
* the version of vcard
|
||||
*/
|
||||
private void appendPhotoStr(byte[] bytes, String type, int version)
|
||||
throws VCardException {
|
||||
String value, encodingStr;
|
||||
try {
|
||||
value = foldingString(new String(Base64.encodeBase64(bytes, true)),
|
||||
version);
|
||||
} catch (Exception e) {
|
||||
throw new VCardException(e.getMessage());
|
||||
}
|
||||
|
||||
if (isNull(type) || type.toUpperCase().indexOf("JPEG") >= 0) {
|
||||
type = "JPEG";
|
||||
} else if (type.toUpperCase().indexOf("GIF") >= 0) {
|
||||
type = "GIF";
|
||||
} else if (type.toUpperCase().indexOf("BMP") >= 0) {
|
||||
type = "BMP";
|
||||
} else {
|
||||
// Handle the string like "image/tiff".
|
||||
int indexOfSlash = type.indexOf("/");
|
||||
if (indexOfSlash >= 0) {
|
||||
type = type.substring(indexOfSlash + 1).toUpperCase();
|
||||
} else {
|
||||
type = type.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
mResult.append("LOGO;TYPE=").append(type);
|
||||
if (version == VERSION_VCARD21_INT) {
|
||||
encodingStr = ";ENCODING=BASE64:";
|
||||
value = value + mNewline;
|
||||
} else if (version == VERSION_VCARD30_INT) {
|
||||
encodingStr = ";ENCODING=b:";
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
mResult.append(encodingStr).append(value).append(mNewline);
|
||||
}
|
||||
|
||||
private boolean isNull(String str) {
|
||||
if (str == null || str.trim().equals("")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build FN and N property. format N's value.
|
||||
*
|
||||
* @param name
|
||||
* the name of the contact
|
||||
*/
|
||||
private void appendNameStr(String name) {
|
||||
mResult.append("FN:").append(name).append(mNewline);
|
||||
mResult.append("N:").append(name).append(mNewline);
|
||||
/*
|
||||
* if(name.indexOf(";") > 0)
|
||||
* mResult.append("N:").append(name).append(mNewline); else
|
||||
* if(name.indexOf(" ") > 0) mResult.append("N:").append(name.replace(' ',
|
||||
* ';')). append(mNewline); else
|
||||
* mResult.append("N:").append(name).append("; ").append(mNewline);
|
||||
*/
|
||||
}
|
||||
|
||||
/** Loop append TEL property. */
|
||||
private void appendPhoneStr(List<ContactStruct.PhoneData> phoneList,
|
||||
int version) {
|
||||
HashMap<String, String> numMap = new HashMap<String, String>();
|
||||
String joinMark = version == VERSION_VCARD21_INT ? ";" : ",";
|
||||
|
||||
for (ContactStruct.PhoneData phone : phoneList) {
|
||||
String type;
|
||||
if (!isNull(phone.data)) {
|
||||
type = getPhoneTypeStr(phone);
|
||||
if (version == VERSION_VCARD30_INT && type.indexOf(";") != -1) {
|
||||
type = type.replace(";", ",");
|
||||
}
|
||||
if (numMap.containsKey(phone.data)) {
|
||||
type = numMap.get(phone.data) + joinMark + type;
|
||||
}
|
||||
numMap.put(phone.data, type);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> num : numMap.entrySet()) {
|
||||
if (version == VERSION_VCARD21_INT) {
|
||||
mResult.append("TEL;");
|
||||
} else { // vcard3.0
|
||||
mResult.append("TEL;TYPE=");
|
||||
}
|
||||
mResult.append(num.getValue()).append(":").append(num.getKey())
|
||||
.append(mNewline);
|
||||
}
|
||||
}
|
||||
|
||||
private String getPhoneTypeStr(PhoneData phone) {
|
||||
|
||||
int phoneType = phone.type;
|
||||
String typeStr, label;
|
||||
|
||||
if (phoneTypeMap.containsKey(phoneType)) {
|
||||
typeStr = phoneTypeMap.get(phoneType);
|
||||
} else if (phoneType == Contacts.Phones.TYPE_CUSTOM) {
|
||||
label = phone.label.toUpperCase();
|
||||
if (phoneTypes.contains(label) || label.startsWith("X-")) {
|
||||
typeStr = label;
|
||||
} else {
|
||||
typeStr = "X-CUSTOM-" + label;
|
||||
}
|
||||
} else {
|
||||
// TODO: need be updated with the provider's future changes
|
||||
typeStr = "VOICE"; // the default type is VOICE in spec.
|
||||
}
|
||||
return typeStr;
|
||||
}
|
||||
|
||||
/** Loop append ADR / EMAIL property. */
|
||||
private void appendContactMethodStr(
|
||||
List<ContactStruct.ContactMethod> contactMList, int version) {
|
||||
|
||||
HashMap<String, String> emailMap = new HashMap<String, String>();
|
||||
String joinMark = version == VERSION_VCARD21_INT ? ";" : ",";
|
||||
for (ContactStruct.ContactMethod contactMethod : contactMList) {
|
||||
// same with v2.1 and v3.0
|
||||
switch (contactMethod.kind) {
|
||||
case Contacts.KIND_EMAIL:
|
||||
String mailType = "INTERNET";
|
||||
if (!isNull(contactMethod.data)) {
|
||||
int methodType = new Integer(contactMethod.type).intValue();
|
||||
if (emailTypeMap.containsKey(methodType)) {
|
||||
mailType = emailTypeMap.get(methodType);
|
||||
} else if (emailTypes.contains(contactMethod.label
|
||||
.toUpperCase())) {
|
||||
mailType = contactMethod.label.toUpperCase();
|
||||
}
|
||||
if (emailMap.containsKey(contactMethod.data)) {
|
||||
mailType = emailMap.get(contactMethod.data) + joinMark
|
||||
+ mailType;
|
||||
}
|
||||
emailMap.put(contactMethod.data, mailType);
|
||||
}
|
||||
break;
|
||||
case Contacts.KIND_POSTAL:
|
||||
if (!isNull(contactMethod.data)) {
|
||||
mResult.append("ADR;TYPE=POSTAL:").append(
|
||||
foldingString(contactMethod.data, version)).append(
|
||||
mNewline);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, String> email : emailMap.entrySet()) {
|
||||
if (version == VERSION_VCARD21_INT) {
|
||||
mResult.append("EMAIL;");
|
||||
} else {
|
||||
mResult.append("EMAIL;TYPE=");
|
||||
}
|
||||
mResult.append(email.getValue()).append(":").append(email.getKey())
|
||||
.append(mNewline);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,447 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.vcard;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.AbstractSyncableContentProvider;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.IContentProvider;
|
||||
import android.os.Handler;
|
||||
import android.provider.Contacts;
|
||||
import android.syncml.pim.PropertyNode;
|
||||
import android.syncml.pim.VBuilder;
|
||||
import android.syncml.pim.VNode;
|
||||
import android.syncml.pim.VParser;
|
||||
import android.text.TextUtils;
|
||||
import android.util.CharsetUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.net.QuotedPrintableCodec;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* VBuilder for VCard. VCard may contain big photo images encoded by BASE64,
|
||||
* If we store all VNode entries in memory like VDataBuilder.java,
|
||||
* OutOfMemoryError may be thrown. Thus, this class push each VCard entry into
|
||||
* ContentResolver immediately.
|
||||
*/
|
||||
@Deprecated
|
||||
public class VCardDataBuilder implements VBuilder {
|
||||
static private String LOG_TAG = "VCardDataBuilder";
|
||||
|
||||
/**
|
||||
* If there's no other information available, this class uses this charset for encoding
|
||||
* byte arrays.
|
||||
*/
|
||||
static public String DEFAULT_CHARSET = "UTF-8";
|
||||
|
||||
private class ProgressShower implements Runnable {
|
||||
private ContactStruct mContact;
|
||||
|
||||
public ProgressShower(ContactStruct contact) {
|
||||
mContact = contact;
|
||||
}
|
||||
|
||||
public void run () {
|
||||
mProgressDialog.setMessage(mProgressMessage + "\n" +
|
||||
mContact.displayString());
|
||||
}
|
||||
}
|
||||
|
||||
/** type=VNode */
|
||||
private VNode mCurrentVNode;
|
||||
private PropertyNode mCurrentPropNode;
|
||||
private String mCurrentParamType;
|
||||
|
||||
/**
|
||||
* The charset using which VParser parses the text.
|
||||
*/
|
||||
private String mSourceCharset;
|
||||
|
||||
/**
|
||||
* The charset with which byte array is encoded to String.
|
||||
*/
|
||||
private String mTargetCharset;
|
||||
private boolean mStrictLineBreakParsing;
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
// For letting VCardDataBuilder show the display name of VCard while handling it.
|
||||
private Handler mHandler;
|
||||
private ProgressDialog mProgressDialog;
|
||||
private String mProgressMessage;
|
||||
private Runnable mOnProgressRunnable;
|
||||
private boolean mLastNameComesBeforeFirstName;
|
||||
|
||||
// Just for testing.
|
||||
private long mTimeCreateContactStruct;
|
||||
private long mTimePushIntoContentResolver;
|
||||
|
||||
// Ideally, this should be ContactsProvider but it seems Class loader cannot find it,
|
||||
// even when it is subclass of ContactsProvider...
|
||||
private AbstractSyncableContentProvider mProvider;
|
||||
private long mMyContactsGroupId;
|
||||
|
||||
public VCardDataBuilder(ContentResolver resolver) {
|
||||
mTargetCharset = DEFAULT_CHARSET;
|
||||
mContentResolver = resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which requires minimum requiredvariables.
|
||||
*
|
||||
* @param resolver insert each data into this ContentResolver
|
||||
* @param progressDialog
|
||||
* @param progressMessage
|
||||
* @param handler if this importer works on the different thread than main one,
|
||||
* set appropriate handler object. If not, it is ok to set this null.
|
||||
*/
|
||||
public VCardDataBuilder(ContentResolver resolver,
|
||||
ProgressDialog progressDialog,
|
||||
String progressMessage,
|
||||
Handler handler) {
|
||||
this(resolver, progressDialog, progressMessage, handler,
|
||||
null, null, false, false);
|
||||
}
|
||||
|
||||
public VCardDataBuilder(ContentResolver resolver,
|
||||
ProgressDialog progressDialog,
|
||||
String progressMessage,
|
||||
Handler handler,
|
||||
String charset,
|
||||
boolean strictLineBreakParsing,
|
||||
boolean lastNameComesBeforeFirstName) {
|
||||
this(resolver, progressDialog, progressMessage, handler,
|
||||
null, charset, strictLineBreakParsing,
|
||||
lastNameComesBeforeFirstName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public VCardDataBuilder(ContentResolver resolver,
|
||||
ProgressDialog progressDialog,
|
||||
String progressMessage,
|
||||
Handler handler,
|
||||
String sourceCharset,
|
||||
String targetCharset,
|
||||
boolean strictLineBreakParsing,
|
||||
boolean lastNameComesBeforeFirstName) {
|
||||
if (sourceCharset != null) {
|
||||
mSourceCharset = sourceCharset;
|
||||
} else {
|
||||
mSourceCharset = VParser.DEFAULT_CHARSET;
|
||||
}
|
||||
if (targetCharset != null) {
|
||||
mTargetCharset = targetCharset;
|
||||
} else {
|
||||
mTargetCharset = DEFAULT_CHARSET;
|
||||
}
|
||||
mContentResolver = resolver;
|
||||
mStrictLineBreakParsing = strictLineBreakParsing;
|
||||
mHandler = handler;
|
||||
mProgressDialog = progressDialog;
|
||||
mProgressMessage = progressMessage;
|
||||
mLastNameComesBeforeFirstName = lastNameComesBeforeFirstName;
|
||||
|
||||
tryGetOriginalProvider();
|
||||
}
|
||||
|
||||
private void tryGetOriginalProvider() {
|
||||
final ContentResolver resolver = mContentResolver;
|
||||
|
||||
if ((mMyContactsGroupId = Contacts.People.tryGetMyContactsGroupId(resolver)) == 0) {
|
||||
Log.e(LOG_TAG, "Could not get group id of MyContact");
|
||||
return;
|
||||
}
|
||||
|
||||
IContentProvider iProviderForName = resolver.acquireProvider(Contacts.CONTENT_URI);
|
||||
ContentProvider contentProvider =
|
||||
ContentProvider.coerceToLocalContentProvider(iProviderForName);
|
||||
if (contentProvider == null) {
|
||||
Log.e(LOG_TAG, "Fail to get ContentProvider object.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(contentProvider instanceof AbstractSyncableContentProvider)) {
|
||||
Log.e(LOG_TAG,
|
||||
"Acquired ContentProvider object is not AbstractSyncableContentProvider.");
|
||||
return;
|
||||
}
|
||||
|
||||
mProvider = (AbstractSyncableContentProvider)contentProvider;
|
||||
}
|
||||
|
||||
public void setOnProgressRunnable(Runnable runnable) {
|
||||
mOnProgressRunnable = runnable;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
}
|
||||
|
||||
public void end() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Assume that VCard is not nested. In other words, this code does not accept
|
||||
*/
|
||||
public void startRecord(String type) {
|
||||
if (mCurrentVNode != null) {
|
||||
// This means startRecord() is called inside startRecord() - endRecord() block.
|
||||
// TODO: should throw some Exception
|
||||
Log.e(LOG_TAG, "Nested VCard code is not supported now.");
|
||||
}
|
||||
mCurrentVNode = new VNode();
|
||||
mCurrentVNode.parseStatus = 1;
|
||||
mCurrentVNode.VName = type;
|
||||
}
|
||||
|
||||
public void endRecord() {
|
||||
mCurrentVNode.parseStatus = 0;
|
||||
long start = System.currentTimeMillis();
|
||||
ContactStruct contact = ContactStruct.constructContactFromVNode(mCurrentVNode,
|
||||
mLastNameComesBeforeFirstName ? ContactStruct.NAME_ORDER_TYPE_JAPANESE :
|
||||
ContactStruct.NAME_ORDER_TYPE_ENGLISH);
|
||||
mTimeCreateContactStruct += System.currentTimeMillis() - start;
|
||||
if (!contact.isIgnorable()) {
|
||||
if (mProgressDialog != null && mProgressMessage != null) {
|
||||
if (mHandler != null) {
|
||||
mHandler.post(new ProgressShower(contact));
|
||||
} else {
|
||||
mProgressDialog.setMessage(mProgressMessage + "\n" +
|
||||
contact.displayString());
|
||||
}
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
if (mProvider != null) {
|
||||
contact.pushIntoAbstractSyncableContentProvider(
|
||||
mProvider, mMyContactsGroupId);
|
||||
} else {
|
||||
contact.pushIntoContentResolver(mContentResolver);
|
||||
}
|
||||
mTimePushIntoContentResolver += System.currentTimeMillis() - start;
|
||||
}
|
||||
if (mOnProgressRunnable != null) {
|
||||
mOnProgressRunnable.run();
|
||||
}
|
||||
mCurrentVNode = null;
|
||||
}
|
||||
|
||||
public void startProperty() {
|
||||
mCurrentPropNode = new PropertyNode();
|
||||
}
|
||||
|
||||
public void endProperty() {
|
||||
mCurrentVNode.propList.add(mCurrentPropNode);
|
||||
mCurrentPropNode = null;
|
||||
}
|
||||
|
||||
public void propertyName(String name) {
|
||||
mCurrentPropNode.propName = name;
|
||||
}
|
||||
|
||||
public void propertyGroup(String group) {
|
||||
mCurrentPropNode.propGroupSet.add(group);
|
||||
}
|
||||
|
||||
public void propertyParamType(String type) {
|
||||
mCurrentParamType = type;
|
||||
}
|
||||
|
||||
public void propertyParamValue(String value) {
|
||||
if (mCurrentParamType == null ||
|
||||
mCurrentParamType.equalsIgnoreCase("TYPE")) {
|
||||
mCurrentPropNode.paramMap_TYPE.add(value);
|
||||
} else {
|
||||
mCurrentPropNode.paramMap.put(mCurrentParamType, value);
|
||||
}
|
||||
|
||||
mCurrentParamType = null;
|
||||
}
|
||||
|
||||
private String encodeString(String originalString, String targetCharset) {
|
||||
if (mSourceCharset.equalsIgnoreCase(targetCharset)) {
|
||||
return originalString;
|
||||
}
|
||||
Charset charset = Charset.forName(mSourceCharset);
|
||||
ByteBuffer byteBuffer = charset.encode(originalString);
|
||||
// byteBuffer.array() "may" return byte array which is larger than
|
||||
// byteBuffer.remaining(). Here, we keep on the safe side.
|
||||
byte[] bytes = new byte[byteBuffer.remaining()];
|
||||
byteBuffer.get(bytes);
|
||||
try {
|
||||
return new String(bytes, targetCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
|
||||
return new String(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
private String handleOneValue(String value, String targetCharset, String encoding) {
|
||||
if (encoding != null) {
|
||||
if (encoding.equals("BASE64") || encoding.equals("B")) {
|
||||
mCurrentPropNode.propValue_bytes =
|
||||
Base64.decodeBase64(value.getBytes());
|
||||
return value;
|
||||
} else if (encoding.equals("QUOTED-PRINTABLE")) {
|
||||
// "= " -> " ", "=\t" -> "\t".
|
||||
// Previous code had done this replacement. Keep on the safe side.
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int length = value.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = value.charAt(i);
|
||||
if (ch == '=' && i < length - 1) {
|
||||
char nextCh = value.charAt(i + 1);
|
||||
if (nextCh == ' ' || nextCh == '\t') {
|
||||
|
||||
builder.append(nextCh);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
builder.append(ch);
|
||||
}
|
||||
String quotedPrintable = builder.toString();
|
||||
|
||||
String[] lines;
|
||||
if (mStrictLineBreakParsing) {
|
||||
lines = quotedPrintable.split("\r\n");
|
||||
} else {
|
||||
builder = new StringBuilder();
|
||||
length = quotedPrintable.length();
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = quotedPrintable.charAt(i);
|
||||
if (ch == '\n') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
} else if (ch == '\r') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
if (i < length - 1) {
|
||||
char nextCh = quotedPrintable.charAt(i + 1);
|
||||
if (nextCh == '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
String finalLine = builder.toString();
|
||||
if (finalLine.length() > 0) {
|
||||
list.add(finalLine);
|
||||
}
|
||||
lines = list.toArray(new String[0]);
|
||||
}
|
||||
|
||||
builder = new StringBuilder();
|
||||
for (String line : lines) {
|
||||
if (line.endsWith("=")) {
|
||||
line = line.substring(0, line.length() - 1);
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = builder.toString().getBytes(mSourceCharset);
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset);
|
||||
bytes = builder.toString().getBytes();
|
||||
}
|
||||
|
||||
try {
|
||||
bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
|
||||
} catch (DecoderException e) {
|
||||
Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
return new String(bytes, targetCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
|
||||
return new String(bytes);
|
||||
}
|
||||
}
|
||||
// Unknown encoding. Fall back to default.
|
||||
}
|
||||
return encodeString(value, targetCharset);
|
||||
}
|
||||
|
||||
public void propertyValues(List<String> values) {
|
||||
if (values == null || values.size() == 0) {
|
||||
mCurrentPropNode.propValue_bytes = null;
|
||||
mCurrentPropNode.propValue_vector.clear();
|
||||
mCurrentPropNode.propValue_vector.add("");
|
||||
mCurrentPropNode.propValue = "";
|
||||
return;
|
||||
}
|
||||
|
||||
ContentValues paramMap = mCurrentPropNode.paramMap;
|
||||
|
||||
String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET"));
|
||||
String encoding = paramMap.getAsString("ENCODING");
|
||||
|
||||
Log.d("@@@", String.format("targetCharset: \"%s\", encoding: \"%s\"",
|
||||
targetCharset, encoding));
|
||||
|
||||
if (TextUtils.isEmpty(targetCharset)) {
|
||||
targetCharset = mTargetCharset;
|
||||
}
|
||||
|
||||
for (String value : values) {
|
||||
mCurrentPropNode.propValue_vector.add(
|
||||
handleOneValue(value, targetCharset, encoding));
|
||||
}
|
||||
|
||||
mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector);
|
||||
}
|
||||
|
||||
public void showDebugInfo() {
|
||||
Log.d(LOG_TAG, "time for creating ContactStruct: " + mTimeCreateContactStruct + " ms");
|
||||
Log.d(LOG_TAG, "time for insert ContactStruct to database: " +
|
||||
mTimePushIntoContentResolver + " ms");
|
||||
}
|
||||
|
||||
private String listToString(List<String> list){
|
||||
int size = list.size();
|
||||
if (size > 1) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int i = 0;
|
||||
for (String type : list) {
|
||||
builder.append(type);
|
||||
if (i < size - 1) {
|
||||
builder.append(";");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
} else if (size == 1) {
|
||||
return list.get(0);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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.vcard;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.syncml.pim.VBuilder;
|
||||
|
||||
@Deprecated
|
||||
public class VCardEntryCounter implements VBuilder {
|
||||
private int mCount;
|
||||
|
||||
public int getCount() {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
}
|
||||
|
||||
public void end() {
|
||||
}
|
||||
|
||||
public void startRecord(String type) {
|
||||
}
|
||||
|
||||
public void endRecord() {
|
||||
mCount++;
|
||||
}
|
||||
|
||||
public void startProperty() {
|
||||
}
|
||||
|
||||
public void endProperty() {
|
||||
}
|
||||
|
||||
public void propertyGroup(String group) {
|
||||
}
|
||||
|
||||
public void propertyName(String name) {
|
||||
}
|
||||
|
||||
public void propertyParamType(String type) {
|
||||
}
|
||||
|
||||
public void propertyParamValue(String value) {
|
||||
}
|
||||
|
||||
public void propertyValues(List<String> values) {
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.vcard;
|
||||
|
||||
@Deprecated
|
||||
public class VCardException extends java.lang.Exception{
|
||||
// constructors
|
||||
|
||||
/**
|
||||
* Constructs a VCardException object
|
||||
*/
|
||||
|
||||
public VCardException()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a VCardException object
|
||||
*
|
||||
* @param message the error message
|
||||
*/
|
||||
|
||||
public VCardException( String message )
|
||||
{
|
||||
super( message );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* 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.vcard;
|
||||
|
||||
/**
|
||||
* VCardException thrown when VCard is nested without VCardParser's being notified.
|
||||
*/
|
||||
@Deprecated
|
||||
public class VCardNestedException extends VCardException {
|
||||
public VCardNestedException() {}
|
||||
public VCardNestedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.vcard;
|
||||
|
||||
import android.syncml.pim.VDataBuilder;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
@Deprecated
|
||||
public class VCardParser {
|
||||
|
||||
// TODO: fix this.
|
||||
VCardParser_V21 mParser = null;
|
||||
|
||||
public final static String VERSION_VCARD21 = "vcard2.1";
|
||||
|
||||
public final static String VERSION_VCARD30 = "vcard3.0";
|
||||
|
||||
final public static int VERSION_VCARD21_INT = 1;
|
||||
|
||||
final public static int VERSION_VCARD30_INT = 2;
|
||||
|
||||
String mVersion = null;
|
||||
|
||||
static final private String TAG = "VCardParser";
|
||||
|
||||
public VCardParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* If version not given. Search from vcard string of the VERSION property.
|
||||
* Then instance mParser to appropriate parser.
|
||||
*
|
||||
* @param vcardStr
|
||||
* the content of vcard data
|
||||
*/
|
||||
private void judgeVersion(String vcardStr) {
|
||||
if (mVersion == null) {// auto judge
|
||||
int verIdx = vcardStr.indexOf("\nVERSION:");
|
||||
if (verIdx == -1) // if not have VERSION, v2.1 default
|
||||
mVersion = VERSION_VCARD21;
|
||||
else {
|
||||
String verStr = vcardStr.substring(verIdx, vcardStr.indexOf(
|
||||
"\n", verIdx + 1));
|
||||
if (verStr.indexOf("2.1") > 0)
|
||||
mVersion = VERSION_VCARD21;
|
||||
else if (verStr.indexOf("3.0") > 0)
|
||||
mVersion = VERSION_VCARD30;
|
||||
else
|
||||
mVersion = VERSION_VCARD21;
|
||||
}
|
||||
}
|
||||
if (mVersion.equals(VERSION_VCARD21))
|
||||
mParser = new VCardParser_V21();
|
||||
if (mVersion.equals(VERSION_VCARD30))
|
||||
mParser = new VCardParser_V30();
|
||||
}
|
||||
|
||||
/**
|
||||
* To make sure the vcard string has proper wrap character
|
||||
*
|
||||
* @param vcardStr
|
||||
* the string to be checked
|
||||
* @return string after verified
|
||||
*/
|
||||
private String verifyVCard(String vcardStr) {
|
||||
this.judgeVersion(vcardStr);
|
||||
// -- indent line:
|
||||
vcardStr = vcardStr.replaceAll("\r\n", "\n");
|
||||
String[] strlist = vcardStr.split("\n");
|
||||
StringBuilder v21str = new StringBuilder("");
|
||||
for (int i = 0; i < strlist.length; i++) {
|
||||
if (strlist[i].indexOf(":") < 0) {
|
||||
if (strlist[i].length() == 0 && strlist[i + 1].indexOf(":") > 0)
|
||||
v21str.append(strlist[i]).append("\r\n");
|
||||
else
|
||||
v21str.append(" ").append(strlist[i]).append("\r\n");
|
||||
} else
|
||||
v21str.append(strlist[i]).append("\r\n");
|
||||
}
|
||||
return v21str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current version
|
||||
*
|
||||
* @param version
|
||||
* the new version
|
||||
*/
|
||||
private void setVersion(String version) {
|
||||
this.mVersion = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given vcard string
|
||||
*
|
||||
* @param vcardStr
|
||||
* to content to be parsed
|
||||
* @param builder
|
||||
* the data builder to hold data
|
||||
* @return true if the string is successfully parsed, else return false
|
||||
* @throws VCardException
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean parse(String vcardStr, VDataBuilder builder)
|
||||
throws VCardException, IOException {
|
||||
|
||||
vcardStr = this.verifyVCard(vcardStr);
|
||||
|
||||
boolean isSuccess = mParser.parse(new ByteArrayInputStream(vcardStr
|
||||
.getBytes()), "US-ASCII", builder);
|
||||
if (!isSuccess) {
|
||||
if (mVersion.equals(VERSION_VCARD21)) {
|
||||
if (Config.LOGD)
|
||||
Log.d(TAG, "Parse failed for vCard 2.1 parser."
|
||||
+ " Try to use 3.0 parser.");
|
||||
|
||||
this.setVersion(VERSION_VCARD30);
|
||||
|
||||
return this.parse(vcardStr, builder);
|
||||
}
|
||||
throw new VCardException("parse failed.(even use 3.0 parser)");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,976 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.vcard;
|
||||
|
||||
import android.syncml.pim.VBuilder;
|
||||
import android.syncml.pim.VParser;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* This class is used to parse vcard. Please refer to vCard Specification 2.1.
|
||||
*/
|
||||
@Deprecated
|
||||
public class VCardParser_V21 {
|
||||
private static final String LOG_TAG = "VCardParser_V21";
|
||||
|
||||
public static final String DEFAULT_CHARSET = VParser.DEFAULT_CHARSET;
|
||||
|
||||
/** Store the known-type */
|
||||
private static final HashSet<String> sKnownTypeSet = new HashSet<String>(
|
||||
Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK",
|
||||
"PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS",
|
||||
"MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK",
|
||||
"ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL",
|
||||
"MCIMAIL", "POWERSHARE", "PRODIGY", "TLX", "X400", "GIF",
|
||||
"CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF",
|
||||
"PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI",
|
||||
"WAVE", "AIFF", "PCM", "X509", "PGP"));
|
||||
|
||||
/** Store the known-value */
|
||||
private static final HashSet<String> sKnownValueSet = new HashSet<String>(
|
||||
Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID"));
|
||||
|
||||
/** Store the property names available in vCard 2.1 */
|
||||
private static final HashSet<String> sAvailablePropertyNameV21 =
|
||||
new HashSet<String>(Arrays.asList(
|
||||
"BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
|
||||
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
|
||||
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER"));
|
||||
|
||||
// Though vCard 2.1 specification does not allow "B" encoding, some data may have it.
|
||||
// We allow it for safety...
|
||||
private static final HashSet<String> sAvailableEncodingV21 =
|
||||
new HashSet<String>(Arrays.asList(
|
||||
"7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B"));
|
||||
|
||||
// Used only for parsing END:VCARD.
|
||||
private String mPreviousLine;
|
||||
|
||||
/** The builder to build parsed data */
|
||||
protected VBuilder mBuilder = null;
|
||||
|
||||
/** The encoding type */
|
||||
protected String mEncoding = null;
|
||||
|
||||
protected final String sDefaultEncoding = "8BIT";
|
||||
|
||||
// Should not directly read a line from this. Use getLine() instead.
|
||||
protected BufferedReader mReader;
|
||||
|
||||
private boolean mCanceled;
|
||||
|
||||
// In some cases, vCard is nested. Currently, we only consider the most interior vCard data.
|
||||
// See v21_foma_1.vcf in test directory for more information.
|
||||
private int mNestCount;
|
||||
|
||||
// In order to reduce warning message as much as possible, we hold the value which made Logger
|
||||
// emit a warning message.
|
||||
protected HashSet<String> mWarningValueMap = new HashSet<String>();
|
||||
|
||||
// Just for debugging
|
||||
private long mTimeTotal;
|
||||
private long mTimeStartRecord;
|
||||
private long mTimeEndRecord;
|
||||
private long mTimeStartProperty;
|
||||
private long mTimeEndProperty;
|
||||
private long mTimeParseItems;
|
||||
private long mTimeParseItem1;
|
||||
private long mTimeParseItem2;
|
||||
private long mTimeParseItem3;
|
||||
private long mTimeHandlePropertyValue1;
|
||||
private long mTimeHandlePropertyValue2;
|
||||
private long mTimeHandlePropertyValue3;
|
||||
|
||||
/**
|
||||
* Create a new VCard parser.
|
||||
*/
|
||||
public VCardParser_V21() {
|
||||
super();
|
||||
}
|
||||
|
||||
public VCardParser_V21(VCardSourceDetector detector) {
|
||||
super();
|
||||
if (detector != null && detector.getType() == VCardSourceDetector.TYPE_FOMA) {
|
||||
mNestCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the file at the given position
|
||||
* vcard_file = [wsls] vcard [wsls]
|
||||
*/
|
||||
protected void parseVCardFile() throws IOException, VCardException {
|
||||
boolean firstReading = true;
|
||||
while (true) {
|
||||
if (mCanceled) {
|
||||
break;
|
||||
}
|
||||
if (!parseOneVCard(firstReading)) {
|
||||
break;
|
||||
}
|
||||
firstReading = false;
|
||||
}
|
||||
|
||||
if (mNestCount > 0) {
|
||||
boolean useCache = true;
|
||||
for (int i = 0; i < mNestCount; i++) {
|
||||
readEndVCard(useCache, true);
|
||||
useCache = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String getVersion() {
|
||||
return "2.1";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when the propertyName is a valid property name.
|
||||
*/
|
||||
protected boolean isValidPropertyName(String propertyName) {
|
||||
if (!(sAvailablePropertyNameV21.contains(propertyName.toUpperCase()) ||
|
||||
propertyName.startsWith("X-")) &&
|
||||
!mWarningValueMap.contains(propertyName)) {
|
||||
mWarningValueMap.add(propertyName);
|
||||
Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when the encoding is a valid encoding.
|
||||
*/
|
||||
protected boolean isValidEncoding(String encoding) {
|
||||
return sAvailableEncodingV21.contains(encoding.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String. It may be null, or its length may be 0
|
||||
* @throws IOException
|
||||
*/
|
||||
protected String getLine() throws IOException {
|
||||
return mReader.readLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String with it's length > 0
|
||||
* @throws IOException
|
||||
* @throws VCardException when the stream reached end of line
|
||||
*/
|
||||
protected String getNonEmptyLine() throws IOException, VCardException {
|
||||
String line;
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException("Reached end of buffer.");
|
||||
} else if (line.trim().length() > 0) {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
|
||||
* items *CRLF
|
||||
* "END" [ws] ":" [ws] "VCARD"
|
||||
*/
|
||||
private boolean parseOneVCard(boolean firstReading) throws IOException, VCardException {
|
||||
boolean allowGarbage = false;
|
||||
if (firstReading) {
|
||||
if (mNestCount > 0) {
|
||||
for (int i = 0; i < mNestCount; i++) {
|
||||
if (!readBeginVCard(allowGarbage)) {
|
||||
return false;
|
||||
}
|
||||
allowGarbage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!readBeginVCard(allowGarbage)) {
|
||||
return false;
|
||||
}
|
||||
long start;
|
||||
if (mBuilder != null) {
|
||||
start = System.currentTimeMillis();
|
||||
mBuilder.startRecord("VCARD");
|
||||
mTimeStartRecord += System.currentTimeMillis() - start;
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
parseItems();
|
||||
mTimeParseItems += System.currentTimeMillis() - start;
|
||||
readEndVCard(true, false);
|
||||
if (mBuilder != null) {
|
||||
start = System.currentTimeMillis();
|
||||
mBuilder.endRecord();
|
||||
mTimeEndRecord += System.currentTimeMillis() - start;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True when successful. False when reaching the end of line
|
||||
* @throws IOException
|
||||
* @throws VCardException
|
||||
*/
|
||||
protected boolean readBeginVCard(boolean allowGarbage)
|
||||
throws IOException, VCardException {
|
||||
String line;
|
||||
do {
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
return false;
|
||||
} else if (line.trim().length() > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
String[] strArray = line.split(":", 2);
|
||||
int length = strArray.length;
|
||||
|
||||
// Though vCard 2.1/3.0 specification does not allow lower cases,
|
||||
// some data may have them, so we allow it (Actually, previous code
|
||||
// had explicitly allowed "BEGIN:vCard" though there's no example).
|
||||
//
|
||||
// TODO: ignore non vCard entry (e.g. vcalendar).
|
||||
// XXX: Not sure, but according to VDataBuilder.java, vcalendar
|
||||
// entry
|
||||
// may be nested. Just seeking "END:SOMETHING" may not be enough.
|
||||
// e.g.
|
||||
// BEGIN:VCARD
|
||||
// ... (Valid. Must parse this)
|
||||
// END:VCARD
|
||||
// BEGIN:VSOMETHING
|
||||
// ... (Must ignore this)
|
||||
// BEGIN:VSOMETHING2
|
||||
// ... (Must ignore this)
|
||||
// END:VSOMETHING2
|
||||
// ... (Must ignore this!)
|
||||
// END:VSOMETHING
|
||||
// BEGIN:VCARD
|
||||
// ... (Valid. Must parse this)
|
||||
// END:VCARD
|
||||
// INVALID_STRING (VCardException should be thrown)
|
||||
if (length == 2 &&
|
||||
strArray[0].trim().equalsIgnoreCase("BEGIN") &&
|
||||
strArray[1].trim().equalsIgnoreCase("VCARD")) {
|
||||
return true;
|
||||
} else if (!allowGarbage) {
|
||||
if (mNestCount > 0) {
|
||||
mPreviousLine = line;
|
||||
return false;
|
||||
} else {
|
||||
throw new VCardException(
|
||||
"Expected String \"BEGIN:VCARD\" did not come "
|
||||
+ "(Instead, \"" + line + "\" came)");
|
||||
}
|
||||
}
|
||||
} while(allowGarbage);
|
||||
|
||||
throw new VCardException("Reached where must not be reached.");
|
||||
}
|
||||
|
||||
/**
|
||||
* The arguments useCache and allowGarbase are usually true and false accordingly when
|
||||
* this function is called outside this function itself.
|
||||
*
|
||||
* @param useCache When true, line is obtained from mPreviousline. Otherwise, getLine()
|
||||
* is used.
|
||||
* @param allowGarbage When true, ignore non "END:VCARD" line.
|
||||
* @throws IOException
|
||||
* @throws VCardException
|
||||
*/
|
||||
protected void readEndVCard(boolean useCache, boolean allowGarbage)
|
||||
throws IOException, VCardException {
|
||||
String line;
|
||||
do {
|
||||
if (useCache) {
|
||||
// Though vCard specification does not allow lower cases,
|
||||
// some data may have them, so we allow it.
|
||||
line = mPreviousLine;
|
||||
} else {
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException("Expected END:VCARD was not found.");
|
||||
} else if (line.trim().length() > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String[] strArray = line.split(":", 2);
|
||||
if (strArray.length == 2 &&
|
||||
strArray[0].trim().equalsIgnoreCase("END") &&
|
||||
strArray[1].trim().equalsIgnoreCase("VCARD")) {
|
||||
return;
|
||||
} else if (!allowGarbage) {
|
||||
throw new VCardException("END:VCARD != \"" + mPreviousLine + "\"");
|
||||
}
|
||||
useCache = false;
|
||||
} while (allowGarbage);
|
||||
}
|
||||
|
||||
/**
|
||||
* items = *CRLF item
|
||||
* / item
|
||||
*/
|
||||
protected void parseItems() throws IOException, VCardException {
|
||||
/* items *CRLF item / item */
|
||||
boolean ended = false;
|
||||
|
||||
if (mBuilder != null) {
|
||||
long start = System.currentTimeMillis();
|
||||
mBuilder.startProperty();
|
||||
mTimeStartProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
ended = parseItem();
|
||||
if (mBuilder != null && !ended) {
|
||||
long start = System.currentTimeMillis();
|
||||
mBuilder.endProperty();
|
||||
mTimeEndProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
|
||||
while (!ended) {
|
||||
// follow VCARD ,it wont reach endProperty
|
||||
if (mBuilder != null) {
|
||||
long start = System.currentTimeMillis();
|
||||
mBuilder.startProperty();
|
||||
mTimeStartProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
ended = parseItem();
|
||||
if (mBuilder != null && !ended) {
|
||||
long start = System.currentTimeMillis();
|
||||
mBuilder.endProperty();
|
||||
mTimeEndProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* item = [groups "."] name [params] ":" value CRLF
|
||||
* / [groups "."] "ADR" [params] ":" addressparts CRLF
|
||||
* / [groups "."] "ORG" [params] ":" orgparts CRLF
|
||||
* / [groups "."] "N" [params] ":" nameparts CRLF
|
||||
* / [groups "."] "AGENT" [params] ":" vcard CRLF
|
||||
*/
|
||||
protected boolean parseItem() throws IOException, VCardException {
|
||||
mEncoding = sDefaultEncoding;
|
||||
|
||||
String line = getNonEmptyLine();
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
String[] propertyNameAndValue = separateLineAndHandleGroup(line);
|
||||
if (propertyNameAndValue == null) {
|
||||
return true;
|
||||
}
|
||||
if (propertyNameAndValue.length != 2) {
|
||||
throw new VCardException("Invalid line \"" + line + "\"");
|
||||
}
|
||||
String propertyName = propertyNameAndValue[0].toUpperCase();
|
||||
String propertyValue = propertyNameAndValue[1];
|
||||
|
||||
mTimeParseItem1 += System.currentTimeMillis() - start;
|
||||
|
||||
if (propertyName.equals("ADR") ||
|
||||
propertyName.equals("ORG") ||
|
||||
propertyName.equals("N")) {
|
||||
start = System.currentTimeMillis();
|
||||
handleMultiplePropertyValue(propertyName, propertyValue);
|
||||
mTimeParseItem3 += System.currentTimeMillis() - start;
|
||||
return false;
|
||||
} else if (propertyName.equals("AGENT")) {
|
||||
handleAgent(propertyValue);
|
||||
return false;
|
||||
} else if (isValidPropertyName(propertyName)) {
|
||||
if (propertyName.equals("BEGIN")) {
|
||||
if (propertyValue.equals("VCARD")) {
|
||||
throw new VCardNestedException("This vCard has nested vCard data in it.");
|
||||
} else {
|
||||
throw new VCardException("Unknown BEGIN type: " + propertyValue);
|
||||
}
|
||||
} else if (propertyName.equals("VERSION") &&
|
||||
!propertyValue.equals(getVersion())) {
|
||||
throw new VCardVersionException("Incompatible version: " +
|
||||
propertyValue + " != " + getVersion());
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
handlePropertyValue(propertyName, propertyValue);
|
||||
mTimeParseItem2 += System.currentTimeMillis() - start;
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new VCardException("Unknown property name: \"" +
|
||||
propertyName + "\"");
|
||||
}
|
||||
|
||||
static private final int STATE_GROUP_OR_PROPNAME = 0;
|
||||
static private final int STATE_PARAMS = 1;
|
||||
// vCard 3.1 specification allows double-quoted param-value, while vCard 2.1 does not.
|
||||
// This is just for safety.
|
||||
static private final int STATE_PARAMS_IN_DQUOTE = 2;
|
||||
|
||||
protected String[] separateLineAndHandleGroup(String line) throws VCardException {
|
||||
int length = line.length();
|
||||
int state = STATE_GROUP_OR_PROPNAME;
|
||||
int nameIndex = 0;
|
||||
|
||||
String[] propertyNameAndValue = new String[2];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = line.charAt(i);
|
||||
switch (state) {
|
||||
case STATE_GROUP_OR_PROPNAME:
|
||||
if (ch == ':') {
|
||||
String propertyName = line.substring(nameIndex, i);
|
||||
if (propertyName.equalsIgnoreCase("END")) {
|
||||
mPreviousLine = line;
|
||||
return null;
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyName(propertyName);
|
||||
}
|
||||
propertyNameAndValue[0] = propertyName;
|
||||
if (i < length - 1) {
|
||||
propertyNameAndValue[1] = line.substring(i + 1);
|
||||
} else {
|
||||
propertyNameAndValue[1] = "";
|
||||
}
|
||||
return propertyNameAndValue;
|
||||
} else if (ch == '.') {
|
||||
String groupName = line.substring(nameIndex, i);
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyGroup(groupName);
|
||||
}
|
||||
nameIndex = i + 1;
|
||||
} else if (ch == ';') {
|
||||
String propertyName = line.substring(nameIndex, i);
|
||||
if (propertyName.equalsIgnoreCase("END")) {
|
||||
mPreviousLine = line;
|
||||
return null;
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyName(propertyName);
|
||||
}
|
||||
propertyNameAndValue[0] = propertyName;
|
||||
nameIndex = i + 1;
|
||||
state = STATE_PARAMS;
|
||||
}
|
||||
break;
|
||||
case STATE_PARAMS:
|
||||
if (ch == '"') {
|
||||
state = STATE_PARAMS_IN_DQUOTE;
|
||||
} else if (ch == ';') {
|
||||
handleParams(line.substring(nameIndex, i));
|
||||
nameIndex = i + 1;
|
||||
} else if (ch == ':') {
|
||||
handleParams(line.substring(nameIndex, i));
|
||||
if (i < length - 1) {
|
||||
propertyNameAndValue[1] = line.substring(i + 1);
|
||||
} else {
|
||||
propertyNameAndValue[1] = "";
|
||||
}
|
||||
return propertyNameAndValue;
|
||||
}
|
||||
break;
|
||||
case STATE_PARAMS_IN_DQUOTE:
|
||||
if (ch == '"') {
|
||||
state = STATE_PARAMS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
throw new VCardException("Invalid line: \"" + line + "\"");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* params = ";" [ws] paramlist
|
||||
* paramlist = paramlist [ws] ";" [ws] param
|
||||
* / param
|
||||
* param = "TYPE" [ws] "=" [ws] ptypeval
|
||||
* / "VALUE" [ws] "=" [ws] pvalueval
|
||||
* / "ENCODING" [ws] "=" [ws] pencodingval
|
||||
* / "CHARSET" [ws] "=" [ws] charsetval
|
||||
* / "LANGUAGE" [ws] "=" [ws] langval
|
||||
* / "X-" word [ws] "=" [ws] word
|
||||
* / knowntype
|
||||
*/
|
||||
protected void handleParams(String params) throws VCardException {
|
||||
String[] strArray = params.split("=", 2);
|
||||
if (strArray.length == 2) {
|
||||
String paramName = strArray[0].trim();
|
||||
String paramValue = strArray[1].trim();
|
||||
if (paramName.equals("TYPE")) {
|
||||
handleType(paramValue);
|
||||
} else if (paramName.equals("VALUE")) {
|
||||
handleValue(paramValue);
|
||||
} else if (paramName.equals("ENCODING")) {
|
||||
handleEncoding(paramValue);
|
||||
} else if (paramName.equals("CHARSET")) {
|
||||
handleCharset(paramValue);
|
||||
} else if (paramName.equals("LANGUAGE")) {
|
||||
handleLanguage(paramValue);
|
||||
} else if (paramName.startsWith("X-")) {
|
||||
handleAnyParam(paramName, paramValue);
|
||||
} else {
|
||||
throw new VCardException("Unknown type \"" + paramName + "\"");
|
||||
}
|
||||
} else {
|
||||
handleType(strArray[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ptypeval = knowntype / "X-" word
|
||||
*/
|
||||
protected void handleType(String ptypeval) {
|
||||
String upperTypeValue = ptypeval;
|
||||
if (!(sKnownTypeSet.contains(upperTypeValue) || upperTypeValue.startsWith("X-")) &&
|
||||
!mWarningValueMap.contains(ptypeval)) {
|
||||
mWarningValueMap.add(ptypeval);
|
||||
Log.w(LOG_TAG, "Type unsupported by vCard 2.1: " + ptypeval);
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("TYPE");
|
||||
mBuilder.propertyParamValue(upperTypeValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
|
||||
*/
|
||||
protected void handleValue(String pvalueval) throws VCardException {
|
||||
if (sKnownValueSet.contains(pvalueval.toUpperCase()) ||
|
||||
pvalueval.startsWith("X-")) {
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("VALUE");
|
||||
mBuilder.propertyParamValue(pvalueval);
|
||||
}
|
||||
} else {
|
||||
throw new VCardException("Unknown value \"" + pvalueval + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
|
||||
*/
|
||||
protected void handleEncoding(String pencodingval) throws VCardException {
|
||||
if (isValidEncoding(pencodingval) ||
|
||||
pencodingval.startsWith("X-")) {
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("ENCODING");
|
||||
mBuilder.propertyParamValue(pencodingval);
|
||||
}
|
||||
mEncoding = pencodingval;
|
||||
} else {
|
||||
throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
|
||||
* but some vCard contains other charset, so we allow them.
|
||||
*/
|
||||
protected void handleCharset(String charsetval) {
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("CHARSET");
|
||||
mBuilder.propertyParamValue(charsetval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See also Section 7.1 of RFC 1521
|
||||
*/
|
||||
protected void handleLanguage(String langval) throws VCardException {
|
||||
String[] strArray = langval.split("-");
|
||||
if (strArray.length != 2) {
|
||||
throw new VCardException("Invalid Language: \"" + langval + "\"");
|
||||
}
|
||||
String tmp = strArray[0];
|
||||
int length = tmp.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!isLetter(tmp.charAt(i))) {
|
||||
throw new VCardException("Invalid Language: \"" + langval + "\"");
|
||||
}
|
||||
}
|
||||
tmp = strArray[1];
|
||||
length = tmp.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!isLetter(tmp.charAt(i))) {
|
||||
throw new VCardException("Invalid Language: \"" + langval + "\"");
|
||||
}
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("LANGUAGE");
|
||||
mBuilder.propertyParamValue(langval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mainly for "X-" type. This accepts any kind of type without check.
|
||||
*/
|
||||
protected void handleAnyParam(String paramName, String paramValue) {
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType(paramName);
|
||||
mBuilder.propertyParamValue(paramValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handlePropertyValue(
|
||||
String propertyName, String propertyValue) throws
|
||||
IOException, VCardException {
|
||||
if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
|
||||
long start = System.currentTimeMillis();
|
||||
String result = getQuotedPrintable(propertyValue);
|
||||
if (mBuilder != null) {
|
||||
ArrayList<String> v = new ArrayList<String>();
|
||||
v.add(result);
|
||||
mBuilder.propertyValues(v);
|
||||
}
|
||||
mTimeHandlePropertyValue2 += System.currentTimeMillis() - start;
|
||||
} else if (mEncoding.equalsIgnoreCase("BASE64") ||
|
||||
mEncoding.equalsIgnoreCase("B")) {
|
||||
long start = System.currentTimeMillis();
|
||||
// It is very rare, but some BASE64 data may be so big that
|
||||
// OutOfMemoryError occurs. To ignore such cases, use try-catch.
|
||||
try {
|
||||
String result = getBase64(propertyValue);
|
||||
if (mBuilder != null) {
|
||||
ArrayList<String> v = new ArrayList<String>();
|
||||
v.add(result);
|
||||
mBuilder.propertyValues(v);
|
||||
}
|
||||
} catch (OutOfMemoryError error) {
|
||||
Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyValues(null);
|
||||
}
|
||||
}
|
||||
mTimeHandlePropertyValue3 += System.currentTimeMillis() - start;
|
||||
} else {
|
||||
if (!(mEncoding == null || mEncoding.equalsIgnoreCase("7BIT")
|
||||
|| mEncoding.equalsIgnoreCase("8BIT")
|
||||
|| mEncoding.toUpperCase().startsWith("X-"))) {
|
||||
Log.w(LOG_TAG, "The encoding unsupported by vCard spec: \"" + mEncoding + "\".");
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
if (mBuilder != null) {
|
||||
ArrayList<String> v = new ArrayList<String>();
|
||||
v.add(maybeUnescapeText(propertyValue));
|
||||
mBuilder.propertyValues(v);
|
||||
}
|
||||
mTimeHandlePropertyValue1 += System.currentTimeMillis() - start;
|
||||
}
|
||||
}
|
||||
|
||||
protected String getQuotedPrintable(String firstString) throws IOException, VCardException {
|
||||
// Specifically, there may be some padding between = and CRLF.
|
||||
// See the following:
|
||||
//
|
||||
// qp-line := *(qp-segment transport-padding CRLF)
|
||||
// qp-part transport-padding
|
||||
// qp-segment := qp-section *(SPACE / TAB) "="
|
||||
// ; Maximum length of 76 characters
|
||||
//
|
||||
// e.g. (from RFC 2045)
|
||||
// Now's the time =
|
||||
// for all folk to come=
|
||||
// to the aid of their country.
|
||||
if (firstString.trim().endsWith("=")) {
|
||||
// remove "transport-padding"
|
||||
int pos = firstString.length() - 1;
|
||||
while(firstString.charAt(pos) != '=') {
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(firstString.substring(0, pos + 1));
|
||||
builder.append("\r\n");
|
||||
String line;
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException(
|
||||
"File ended during parsing quoted-printable String");
|
||||
}
|
||||
if (line.trim().endsWith("=")) {
|
||||
// remove "transport-padding"
|
||||
pos = line.length() - 1;
|
||||
while(line.charAt(pos) != '=') {
|
||||
}
|
||||
builder.append(line.substring(0, pos + 1));
|
||||
builder.append("\r\n");
|
||||
} else {
|
||||
builder.append(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
} else {
|
||||
return firstString;
|
||||
}
|
||||
}
|
||||
|
||||
protected String getBase64(String firstString) throws IOException, VCardException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(firstString);
|
||||
|
||||
while (true) {
|
||||
String line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException(
|
||||
"File ended during parsing BASE64 binary");
|
||||
}
|
||||
if (line.length() == 0) {
|
||||
break;
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mainly for "ADR", "ORG", and "N"
|
||||
* We do not care the number of strnosemi here.
|
||||
*
|
||||
* addressparts = 0*6(strnosemi ";") strnosemi
|
||||
* ; PO Box, Extended Addr, Street, Locality, Region,
|
||||
* Postal Code, Country Name
|
||||
* orgparts = *(strnosemi ";") strnosemi
|
||||
* ; First is Organization Name,
|
||||
* remainder are Organization Units.
|
||||
* nameparts = 0*4(strnosemi ";") strnosemi
|
||||
* ; Family, Given, Middle, Prefix, Suffix.
|
||||
* ; Example:Public;John;Q.;Reverend Dr.;III, Esq.
|
||||
* strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi
|
||||
* ; To include a semicolon in this string, it must be escaped
|
||||
* ; with a "\" character.
|
||||
*
|
||||
* We are not sure whether we should add "\" CRLF to each value.
|
||||
* For now, we exclude them.
|
||||
*/
|
||||
protected void handleMultiplePropertyValue(
|
||||
String propertyName, String propertyValue) throws IOException, VCardException {
|
||||
// vCard 2.1 does not allow QUOTED-PRINTABLE here, but some data have it.
|
||||
if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
|
||||
propertyValue = getQuotedPrintable(propertyValue);
|
||||
}
|
||||
|
||||
if (mBuilder != null) {
|
||||
// TODO: limit should be set in accordance with propertyName?
|
||||
StringBuilder builder = new StringBuilder();
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
int length = propertyValue.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = propertyValue.charAt(i);
|
||||
if (ch == '\\' && i < length - 1) {
|
||||
char nextCh = propertyValue.charAt(i + 1);
|
||||
String unescapedString = maybeUnescape(nextCh);
|
||||
if (unescapedString != null) {
|
||||
builder.append(unescapedString);
|
||||
i++;
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
} else if (ch == ';') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
list.add(builder.toString());
|
||||
mBuilder.propertyValues(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 2.1 specifies AGENT allows one vcard entry. It is not encoded at all.
|
||||
*
|
||||
* item = ...
|
||||
* / [groups "."] "AGENT"
|
||||
* [params] ":" vcard CRLF
|
||||
* vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
|
||||
* items *CRLF "END" [ws] ":" [ws] "VCARD"
|
||||
*
|
||||
*/
|
||||
protected void handleAgent(String propertyValue) throws VCardException {
|
||||
throw new VCardException("AGENT Property is not supported.");
|
||||
/* This is insufficient support. Also, AGENT Property is very rare.
|
||||
Ignore it for now.
|
||||
TODO: fix this.
|
||||
|
||||
String[] strArray = propertyValue.split(":", 2);
|
||||
if (!(strArray.length == 2 ||
|
||||
strArray[0].trim().equalsIgnoreCase("BEGIN") &&
|
||||
strArray[1].trim().equalsIgnoreCase("VCARD"))) {
|
||||
throw new VCardException("BEGIN:VCARD != \"" + propertyValue + "\"");
|
||||
}
|
||||
parseItems();
|
||||
readEndVCard();
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* For vCard 3.0.
|
||||
*/
|
||||
protected String maybeUnescapeText(String text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unescaped String if the character should be unescaped. Return null otherwise.
|
||||
* e.g. In vCard 2.1, "\;" should be unescaped into ";" while "\x" should not be.
|
||||
*/
|
||||
protected String maybeUnescape(char ch) {
|
||||
// Original vCard 2.1 specification does not allow transformation
|
||||
// "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous implementation of
|
||||
// this class allowed them, so keep it as is.
|
||||
if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') {
|
||||
return String.valueOf(ch);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given stream and constructs VCardDataBuilder object.
|
||||
* Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets
|
||||
* local encoding to it. For example, Japanese phone career uses Shift_JIS, which
|
||||
* is not formally allowed in vCard specification.
|
||||
* As a result, there is a case where the encoding given here does not do well with
|
||||
* the "CHARSET".
|
||||
*
|
||||
* In order to avoid such cases, It may be fine to use "ISO-8859-1" as an encoding,
|
||||
* and to encode each localized String afterward.
|
||||
*
|
||||
* RFC 2426 "recommends" (not forces) to use UTF-8, so it may be OK to use
|
||||
* UTF-8 as an encoding when parsing vCard 3.0. But note that some Japanese
|
||||
* phone uses Shift_JIS as a charset (e.g. W61SH), and another uses
|
||||
* "CHARSET=SHIFT_JIS", which is explicitly prohibited in vCard 3.0 specification
|
||||
* (e.g. W53K).
|
||||
*
|
||||
* @param is
|
||||
* The source to parse.
|
||||
* @param charset
|
||||
* The charset.
|
||||
* @param builder
|
||||
* The v builder which used to construct data.
|
||||
* @return Return true for success, otherwise false.
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean parse(InputStream is, String charset, VBuilder builder)
|
||||
throws IOException, VCardException {
|
||||
// TODO: make this count error entries instead of just throwing VCardException.
|
||||
|
||||
// TODO: If we really need to allow only CRLF as line break,
|
||||
// we will have to develop our own BufferedReader().
|
||||
mReader = new CustomBufferedReader(new InputStreamReader(is, charset));
|
||||
|
||||
mBuilder = builder;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
if (mBuilder != null) {
|
||||
mBuilder.start();
|
||||
}
|
||||
parseVCardFile();
|
||||
if (mBuilder != null) {
|
||||
mBuilder.end();
|
||||
}
|
||||
mTimeTotal += System.currentTimeMillis() - start;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean parse(InputStream is, VBuilder builder) throws IOException, VCardException {
|
||||
return parse(is, DEFAULT_CHARSET, builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel parsing.
|
||||
* Actual cancel is done after the end of the current one vcard entry parsing.
|
||||
*/
|
||||
public void cancel() {
|
||||
mCanceled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* It is very, very rare case, but there is a case where
|
||||
* canceled may be already true outside this object.
|
||||
* @hide
|
||||
*/
|
||||
public void parse(InputStream is, String charset, VBuilder builder, boolean canceled)
|
||||
throws IOException, VCardException {
|
||||
mCanceled = canceled;
|
||||
parse(is, charset, builder);
|
||||
}
|
||||
|
||||
public void showDebugInfo() {
|
||||
Log.d(LOG_TAG, "total parsing time: " + mTimeTotal + " ms");
|
||||
if (mReader instanceof CustomBufferedReader) {
|
||||
Log.d(LOG_TAG, "total readLine time: " +
|
||||
((CustomBufferedReader)mReader).getTotalmillisecond() + " ms");
|
||||
}
|
||||
Log.d(LOG_TAG, "mTimeStartRecord: " + mTimeStartRecord + " ms");
|
||||
Log.d(LOG_TAG, "mTimeEndRecord: " + mTimeEndRecord + " ms");
|
||||
Log.d(LOG_TAG, "mTimeParseItem1: " + mTimeParseItem1 + " ms");
|
||||
Log.d(LOG_TAG, "mTimeParseItem2: " + mTimeParseItem2 + " ms");
|
||||
Log.d(LOG_TAG, "mTimeParseItem3: " + mTimeParseItem3 + " ms");
|
||||
Log.d(LOG_TAG, "mTimeHandlePropertyValue1: " + mTimeHandlePropertyValue1 + " ms");
|
||||
Log.d(LOG_TAG, "mTimeHandlePropertyValue2: " + mTimeHandlePropertyValue2 + " ms");
|
||||
Log.d(LOG_TAG, "mTimeHandlePropertyValue3: " + mTimeHandlePropertyValue3 + " ms");
|
||||
}
|
||||
|
||||
private boolean isLetter(char ch) {
|
||||
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomBufferedReader extends BufferedReader {
|
||||
private long mTime;
|
||||
|
||||
public CustomBufferedReader(Reader in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readLine() throws IOException {
|
||||
long start = System.currentTimeMillis();
|
||||
String ret = super.readLine();
|
||||
long end = System.currentTimeMillis();
|
||||
mTime += end - start;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public long getTotalmillisecond() {
|
||||
return mTime;
|
||||
}
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.vcard;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* This class is used to parse vcard3.0. <br>
|
||||
* Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426)
|
||||
*/
|
||||
@Deprecated
|
||||
public class VCardParser_V30 extends VCardParser_V21 {
|
||||
private static final String LOG_TAG = "VCardParser_V30";
|
||||
|
||||
private static final HashSet<String> acceptablePropsWithParam = new HashSet<String>(
|
||||
Arrays.asList(
|
||||
"BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
|
||||
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
|
||||
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1
|
||||
"NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS",
|
||||
"SORT-STRING", "CATEGORIES", "PRODID")); // 3.0
|
||||
|
||||
// Although "7bit" and "BASE64" is not allowed in vCard 3.0, we allow it for safety.
|
||||
private static final HashSet<String> sAcceptableEncodingV30 = new HashSet<String>(
|
||||
Arrays.asList("7BIT", "8BIT", "BASE64", "B"));
|
||||
|
||||
// Although RFC 2426 specifies some property must not have parameters, we allow it,
|
||||
// since there may be some careers which violates the RFC...
|
||||
private static final HashSet<String> acceptablePropsWithoutParam = new HashSet<String>();
|
||||
|
||||
private String mPreviousLine;
|
||||
|
||||
@Override
|
||||
protected String getVersion() {
|
||||
return "3.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidPropertyName(String propertyName) {
|
||||
if (!(acceptablePropsWithParam.contains(propertyName) ||
|
||||
acceptablePropsWithoutParam.contains(propertyName) ||
|
||||
propertyName.startsWith("X-")) &&
|
||||
!mWarningValueMap.contains(propertyName)) {
|
||||
mWarningValueMap.add(propertyName);
|
||||
Log.w(LOG_TAG, "Property name unsupported by vCard 3.0: " + propertyName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidEncoding(String encoding) {
|
||||
return sAcceptableEncodingV30.contains(encoding.toUpperCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLine() throws IOException {
|
||||
if (mPreviousLine != null) {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = null;
|
||||
return ret;
|
||||
} else {
|
||||
return mReader.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 requires that the line with space at the beginning of the line
|
||||
* must be combined with previous line.
|
||||
*/
|
||||
@Override
|
||||
protected String getNonEmptyLine() throws IOException, VCardException {
|
||||
String line;
|
||||
StringBuilder builder = null;
|
||||
while (true) {
|
||||
line = mReader.readLine();
|
||||
if (line == null) {
|
||||
if (builder != null) {
|
||||
return builder.toString();
|
||||
} else if (mPreviousLine != null) {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = null;
|
||||
return ret;
|
||||
}
|
||||
throw new VCardException("Reached end of buffer.");
|
||||
} else if (line.length() == 0) {
|
||||
if (builder != null) {
|
||||
return builder.toString();
|
||||
} else if (mPreviousLine != null) {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = null;
|
||||
return ret;
|
||||
}
|
||||
} else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') {
|
||||
if (builder != null) {
|
||||
// See Section 5.8.1 of RFC 2425 (MIME-DIR document).
|
||||
// Following is the excerpts from it.
|
||||
//
|
||||
// DESCRIPTION:This is a long description that exists on a long line.
|
||||
//
|
||||
// Can be represented as:
|
||||
//
|
||||
// DESCRIPTION:This is a long description
|
||||
// that exists on a long line.
|
||||
//
|
||||
// It could also be represented as:
|
||||
//
|
||||
// DESCRIPTION:This is a long descrip
|
||||
// tion that exists o
|
||||
// n a long line.
|
||||
builder.append(line.substring(1));
|
||||
} else if (mPreviousLine != null) {
|
||||
builder = new StringBuilder();
|
||||
builder.append(mPreviousLine);
|
||||
mPreviousLine = null;
|
||||
builder.append(line.substring(1));
|
||||
} else {
|
||||
throw new VCardException("Space exists at the beginning of the line");
|
||||
}
|
||||
} else {
|
||||
if (mPreviousLine == null) {
|
||||
mPreviousLine = line;
|
||||
if (builder != null) {
|
||||
return builder.toString();
|
||||
}
|
||||
} else {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = line;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* vcard = [group "."] "BEGIN" ":" "VCARD" 1*CRLF
|
||||
* 1*(contentline)
|
||||
* ;A vCard object MUST include the VERSION, FN and N types.
|
||||
* [group "."] "END" ":" "VCARD" 1*CRLF
|
||||
*/
|
||||
@Override
|
||||
protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
|
||||
// TODO: vCard 3.0 supports group.
|
||||
return super.readBeginVCard(allowGarbage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readEndVCard(boolean useCache, boolean allowGarbage)
|
||||
throws IOException, VCardException {
|
||||
// TODO: vCard 3.0 supports group.
|
||||
super.readEndVCard(useCache, allowGarbage);
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
|
||||
*/
|
||||
@Override
|
||||
protected void handleParams(String params) throws VCardException {
|
||||
try {
|
||||
super.handleParams(params);
|
||||
} catch (VCardException e) {
|
||||
// maybe IANA type
|
||||
String[] strArray = params.split("=", 2);
|
||||
if (strArray.length == 2) {
|
||||
handleAnyParam(strArray[0], strArray[1]);
|
||||
} else {
|
||||
// Must not come here in the current implementation.
|
||||
throw new VCardException(
|
||||
"Unknown params value: " + params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleAnyParam(String paramName, String paramValue) {
|
||||
// vCard 3.0 accept comma-separated multiple values, but
|
||||
// current PropertyNode does not accept it.
|
||||
// For now, we do not split the values.
|
||||
//
|
||||
// TODO: fix this.
|
||||
super.handleAnyParam(paramName, paramValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 defines
|
||||
*
|
||||
* param = param-name "=" param-value *("," param-value)
|
||||
* param-name = iana-token / x-name
|
||||
* param-value = ptext / quoted-string
|
||||
* quoted-string = DQUOTE QSAFE-CHAR DQUOTE
|
||||
*/
|
||||
@Override
|
||||
protected void handleType(String ptypevalues) {
|
||||
String[] ptypeArray = ptypevalues.split(",");
|
||||
mBuilder.propertyParamType("TYPE");
|
||||
for (String value : ptypeArray) {
|
||||
int length = value.length();
|
||||
if (length >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
|
||||
mBuilder.propertyParamValue(value.substring(1, value.length() - 1));
|
||||
} else {
|
||||
mBuilder.propertyParamValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleAgent(String propertyValue) throws VCardException {
|
||||
// The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.0.
|
||||
//
|
||||
// e.g.
|
||||
// AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
|
||||
// TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
|
||||
// ET:jfriday@host.com\nEND:VCARD\n
|
||||
//
|
||||
// TODO: fix this.
|
||||
//
|
||||
// issue:
|
||||
// vCard 3.0 also allows this as an example.
|
||||
//
|
||||
// AGENT;VALUE=uri:
|
||||
// CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
|
||||
//
|
||||
// This is not VCARD. Should we support this?
|
||||
throw new VCardException("AGENT in vCard 3.0 is not supported yet.");
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 does not require two CRLF at the last of BASE64 data.
|
||||
* It only requires that data should be MIME-encoded.
|
||||
*/
|
||||
@Override
|
||||
protected String getBase64(String firstString) throws IOException, VCardException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(firstString);
|
||||
|
||||
while (true) {
|
||||
String line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException(
|
||||
"File ended during parsing BASE64 binary");
|
||||
}
|
||||
if (line.length() == 0) {
|
||||
break;
|
||||
} else if (!line.startsWith(" ") && !line.startsWith("\t")) {
|
||||
mPreviousLine = line;
|
||||
break;
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
|
||||
* ; \\ encodes \, \n or \N encodes newline
|
||||
* ; \; encodes ;, \, encodes ,
|
||||
*
|
||||
* Note: Apple escape ':' into '\:' while does not escape '\'
|
||||
*/
|
||||
@Override
|
||||
protected String maybeUnescapeText(String text) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int length = text.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = text.charAt(i);
|
||||
if (ch == '\\' && i < length - 1) {
|
||||
char next_ch = text.charAt(++i);
|
||||
if (next_ch == 'n' || next_ch == 'N') {
|
||||
builder.append("\r\n");
|
||||
} else {
|
||||
builder.append(next_ch);
|
||||
}
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String maybeUnescape(char ch) {
|
||||
if (ch == 'n' || ch == 'N') {
|
||||
return "\r\n";
|
||||
} else {
|
||||
return String.valueOf(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* 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.vcard;
|
||||
|
||||
import android.syncml.pim.VBuilder;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Class which tries to detects the source of the vCard from its properties.
|
||||
* Currently this implementation is very premature.
|
||||
*/
|
||||
@Deprecated
|
||||
public class VCardSourceDetector implements VBuilder {
|
||||
// Should only be used in package.
|
||||
static final int TYPE_UNKNOWN = 0;
|
||||
static final int TYPE_APPLE = 1;
|
||||
static final int TYPE_JAPANESE_MOBILE_PHONE = 2; // Used in Japanese mobile phones.
|
||||
static final int TYPE_FOMA = 3; // Used in some Japanese FOMA mobile phones.
|
||||
static final int TYPE_WINDOWS_MOBILE_JP = 4;
|
||||
// TODO: Excel, etc.
|
||||
|
||||
private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
|
||||
"X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME",
|
||||
"X-ABADR", "X-ABUID"));
|
||||
|
||||
private static Set<String> JAPANESE_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
|
||||
"X-GNO", "X-GN", "X-REDUCTION"));
|
||||
|
||||
private static Set<String> WINDOWS_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
|
||||
"X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC"));
|
||||
|
||||
// Note: these signes appears before the signs of the other type (e.g. "X-GN").
|
||||
// In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES.
|
||||
private static Set<String> FOMA_SIGNS = new HashSet<String>(Arrays.asList(
|
||||
"X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED",
|
||||
"X-SD-DESCRIPTION"));
|
||||
private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE";
|
||||
|
||||
private int mType = TYPE_UNKNOWN;
|
||||
// Some mobile phones (like FOMA) tells us the charset of the data.
|
||||
private boolean mNeedParseSpecifiedCharset;
|
||||
private String mSpecifiedCharset;
|
||||
|
||||
public void start() {
|
||||
}
|
||||
|
||||
public void end() {
|
||||
}
|
||||
|
||||
public void startRecord(String type) {
|
||||
}
|
||||
|
||||
public void startProperty() {
|
||||
mNeedParseSpecifiedCharset = false;
|
||||
}
|
||||
|
||||
public void endProperty() {
|
||||
}
|
||||
|
||||
public void endRecord() {
|
||||
}
|
||||
|
||||
public void propertyGroup(String group) {
|
||||
}
|
||||
|
||||
public void propertyName(String name) {
|
||||
if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
|
||||
mType = TYPE_FOMA;
|
||||
mNeedParseSpecifiedCharset = true;
|
||||
return;
|
||||
}
|
||||
if (mType != TYPE_UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) {
|
||||
mType = TYPE_WINDOWS_MOBILE_JP;
|
||||
} else if (FOMA_SIGNS.contains(name)) {
|
||||
mType = TYPE_FOMA;
|
||||
} else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) {
|
||||
mType = TYPE_JAPANESE_MOBILE_PHONE;
|
||||
} else if (APPLE_SIGNS.contains(name)) {
|
||||
mType = TYPE_APPLE;
|
||||
}
|
||||
}
|
||||
|
||||
public void propertyParamType(String type) {
|
||||
}
|
||||
|
||||
public void propertyParamValue(String value) {
|
||||
}
|
||||
|
||||
public void propertyValues(List<String> values) {
|
||||
if (mNeedParseSpecifiedCharset && values.size() > 0) {
|
||||
mSpecifiedCharset = values.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return charset String guessed from the source's properties.
|
||||
* This method must be called after parsing target file(s).
|
||||
* @return Charset String. Null is returned if guessing the source fails.
|
||||
*/
|
||||
public String getEstimatedCharset() {
|
||||
if (mSpecifiedCharset != null) {
|
||||
return mSpecifiedCharset;
|
||||
}
|
||||
switch (mType) {
|
||||
case TYPE_WINDOWS_MOBILE_JP:
|
||||
case TYPE_FOMA:
|
||||
case TYPE_JAPANESE_MOBILE_PHONE:
|
||||
return "SHIFT_JIS";
|
||||
case TYPE_APPLE:
|
||||
return "UTF-8";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* 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.vcard;
|
||||
|
||||
/**
|
||||
* VCardException used only when the version of the vCard is different.
|
||||
*/
|
||||
@Deprecated
|
||||
public class VCardVersionException extends VCardException {
|
||||
public VCardVersionException() {
|
||||
}
|
||||
|
||||
public VCardVersionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<HTML>
|
||||
<BODY>
|
||||
Support classes for SyncML.
|
||||
{@hide}
|
||||
</BODY>
|
||||
</HTML>
|
||||
Reference in New Issue
Block a user