/* * 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.Log; 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.Collection; import java.util.List; import java.util.Vector; /** * 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. */ public class VDataBuilder implements VBuilder { static private String LOG_TAG = "VDATABuilder"; /** type=VNode */ public List<VNode> vNodeList = new ArrayList<VNode>(); private int mNodeListPos = 0; private VNode mCurrentVNode; private PropertyNode mCurrentPropNode; private String mCurrentParamType; /** * Assumes that each String can be encoded into byte array using this encoding. */ private String mCharset; private boolean mStrictLineBreakParsing; public VDataBuilder() { mCharset = "ISO-8859-1"; mStrictLineBreakParsing = false; } public VDataBuilder(String encoding, boolean strictLineBreakParsing) { mCharset = encoding; mStrictLineBreakParsing = strictLineBreakParsing; } public void start() { } public void end() { } public void startRecord(String type) { VNode vnode = new VNode(); vnode.parseStatus = 1; vnode.VName = type; 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() { // System.out.println("+ startProperty. "); } public void endProperty() { // System.out.println("- endProperty. "); } public void propertyName(String name) { mCurrentPropNode = new PropertyNode(); 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 targetEncoding) { Charset charset = Charset.forName(mCharset); 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, targetEncoding); } catch (UnsupportedEncodingException e) { return null; } } public void propertyValues(List<String> values) { ContentValues paramMap = mCurrentPropNode.paramMap; String charsetString = paramMap.getAsString("CHARSET"); boolean setupParamValues = false; //decode value string to propValue_bytes if (paramMap.containsKey("ENCODING")) { String encoding = paramMap.getAsString("ENCODING"); if (encoding.equalsIgnoreCase("BASE64") || encoding.equalsIgnoreCase("B")) { if (values.size() > 1) { Log.e(LOG_TAG, ("BASE64 encoding is used while " + "there are multiple values (" + values.size())); } mCurrentPropNode.propValue_bytes = Base64.decodeBase64(values.get(0). replaceAll(" ","").replaceAll("\t",""). replaceAll("\r\n",""). getBytes()); } if(encoding.equalsIgnoreCase("QUOTED-PRINTABLE")){ // if CHARSET is defined, we translate each String into the Charset. List<String> tmpValues = new ArrayList<String>(); Vector<byte[]> byteVector = new Vector<byte[]>(); int size = 0; try{ for (String value : values) { String quotedPrintable = value .replaceAll("= ", " ").replaceAll("=\t", "\t"); String[] lines; if (mStrictLineBreakParsing) { lines = quotedPrintable.split("\r\n"); } else { lines = quotedPrintable .replace("\r\n", "\n").replace("\r", "\n").split("\n"); } StringBuilder builder = new StringBuilder(); for (String line : lines) { if (line.endsWith("=")) { line = line.substring(0, line.length() - 1); } builder.append(line); } byte[] bytes = QuotedPrintableCodec.decodeQuotedPrintable( builder.toString().getBytes()); if (charsetString != null) { try { tmpValues.add(new String(bytes, charsetString)); } catch (UnsupportedEncodingException e) { Log.e(LOG_TAG, "Failed to encode: charset=" + charsetString); tmpValues.add(new String(bytes)); } } else { tmpValues.add(new String(bytes)); } byteVector.add(bytes); size += bytes.length; } // for (String value : values) { mCurrentPropNode.propValue_vector = tmpValues; mCurrentPropNode.propValue = listToString(tmpValues); mCurrentPropNode.propValue_bytes = new byte[size]; { byte[] tmpBytes = mCurrentPropNode.propValue_bytes; int index = 0; for (byte[] bytes : byteVector) { int length = bytes.length; for (int i = 0; i < length; i++, index++) { tmpBytes[index] = bytes[i]; } } } setupParamValues = true; } catch(Exception e) { Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); } } // QUOTED-PRINTABLE } // ENCODING if (!setupParamValues) { // if CHARSET is defined, we translate each String into the Charset. if (charsetString != null) { List<String> tmpValues = new ArrayList<String>(); for (String value : values) { String result = encodeString(value, charsetString); if (result != null) { tmpValues.add(result); } else { Log.e(LOG_TAG, "Failed to encode: charset=" + charsetString); tmpValues.add(value); } } values = tmpValues; } mCurrentPropNode.propValue_vector = values; mCurrentPropNode.propValue = listToString(values); } mCurrentVNode.propList.add(mCurrentPropNode); } private String listToString(Collection<String> list){ 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(); } public String getResult(){ return null; } }